IT rekvalifikácia. Seniorní programátori zarábajú až 6 000 €/mesiac a rekvalifikácia je prvým krokom. Zisti, ako na to!

3. diel - Hracia kocka - Konštruktory a náhodné čísla vo Swift

V predchádzajúcom cvičení, Riešené úlohy k 1.-2. lekciu OOP vo Swift, sme si precvičili získané skúsenosti z predchádzajúcich lekcií.

V minulej lekcii, Riešené úlohy k 1.-2. lekciu OOP vo Swift , sme si naprogramovali prvú objektovú aplikáciu. Už vieme tvoriť nové triedy a vkladať do nich vlastnosti a metódy s parametrami a návratovú hodnotou. V dnešnom Swift tutoriálu začneme pracovať na sľúbené aréne, v ktorej budú proti sebe bojovať dvaja bojovníci. Boj bude ťahový (na preskáčku) a bojovník vždy druhému uberie život na základe sily jeho útoku a obrany druhého bojovníka. Simulujeme v podstate stolný hru, budeme teda simulovať aj hraciu kocku, ktorá dodá hre prvok náhodnosti. Začnime zvoľna a vytvorme si dnes práve túto hraciu kocku. Zároveň sa naučíme ako definovať vlastné konštruktor.

Vytvorme si novú Command Line Tool aplikáciu a pomenujte ju Arena. K projektu si pridajme novú class s názvom Kostka. Zamyslime sa nad vlastnosťami, ktoré kocke dáme. Iste by sa hodilo, keby sme si mohli zvoliť počet stien kocky (klasicky 6 alebo 10 stien, ako je zvykom u tohto typu hier). Ďalej bude kocka potrebovať tzv. Generátor náhodných čísel. Ten nám samozrejme poskytne Swift, ktorý na tieto účely obsahuje špeciálnu funkciu. Naša trieda bude mať teraz zatiaľ vlastnosť pocetStran, ktorú pre východiskový stav rovno nastavíme na 6, nech nemusíme riešiť Optional alebo písať konštruktor.

Minule sme kvôli jednoduchosti nastavovali všetky vlastnosti našej triedy ako public, teda ako verejne prístupné. Väčšinou sa však skôr nechce, aby sa dali zvonku modifikovať a používa sa modifikátor private. Vlastnosť je potom viditeľná len vnútri triedy a zvonku sa Swift tvári, že vôbec neexistuje. Pri návrhu triedy teda nastavíme všetko na private a v prípade, že niečo bude naozaj potrebné vystaviť, zmažeme private a tým sa použije internal (takže pre celý modul). Naša trieda teraz vyzerá asi takto:

class Kostka {
    private var pocetSten = 6

}

Konštruktory

Až doteraz sme nevedeli zvonku nastaviť iné vlastnosti ako public, pretože napr. private nie sú zvonku viditeľné. Už sme si hovorili niečo málo o konstruktoru objektu. Je to metóda, ktorá sa zavolá vo chvíli vytvorenia inštancie objektu. Slúži samozrejme k nastavenie vnútorného stavu objektu a na vykonanie prípadnej inicializácia. Kocku by sme teraz v main.swift vytvorili takto:

let kostka = Kostka()

Práve Kostka() je konštruktor. Pretože v našej triede žiadny nie je, Swift si dogeneruje prázdnu metódu. My si však teraz konštruktor do triedy pridáme. Deklaruje sa ako metóda, ale nemá návratový typ a musí mať názov init(). V konstruktoru nastavíme počet stien na pevnú hodnotu aa tým pádom už nemusíme mať pocetSten nastavený priamo v deklarácii, je ale treba pridať dátový typ. Trieda Kostka bude vyzerať nasledovne:

private var pocetSten : Int

init() {
        pocetSten = 6
}

Ak kocku teraz vytvoríme, bude mať vo vlastnosti pocetSten hodnotu 6. Vypíšme si počet stien do konzoly, nech vidíme, že tam hodnota naozaj je. Nie je dobré vlastnosť nastaviť na public / internal, pretože nebudeme chcieť, aby nám niekto mohol už u vytvorenej kocky meniť počet stien.

Pridáme do triedy teda metódu vratPocetSten(), ktorá nám vráti hodnotu vlastnosti pocetSten. Docielili sme tým v podstate toho, že je vlastnosť read-only (vlastnosť nie je viditeľná a je možné ju iba čítať metódou, zmeniť ju nemožno). Swift má na tento účel ešte ďalšie konštrukcie, ale tým sa zatiaľ nebudeme zaoberať. Nová metóda bude vyzerať asi takto:

func vratPocetSten() -> Int {
    return pocetSten
}

Presuňme sa do main.swift a vyskúšajme si vytvoriť kocku a vypísať počet stien:

let kostka = Kostka() // v tuto chvíli se zavolá konstruktor
print(kostka.vratPocetSten())
class Kostka {
    private var pocetSten : Int

    init() {
        pocetSten = 6
    }

    func vratPocetSten() -> Int {
        return pocetSten
    }
}

výstup:

6

Vidíme, že sa konštruktor naozaj zavolal. My by sme ale chceli, aby sme mohli pri každej kocky pri vytvorení špecifikovať, koľko stien budeme potrebovať. Dáme teda konstruktoru parameter:

init(aPocetSten: Int) {
    pocetSten = aPocetSten
}

Všimnite si, že sme pred názov parametra pridali a, aby sme ho odlíšili od rovnomennej vlastnosti triedy. Vráťme sa k main.swift a zadajte tento parameter do konstruktoru:

let kostka = Kostka(aPocetSten: 10)
print(kostka.vratPocetSten())
class Kostka {
    private var pocetSten : Int

    init(aPocetSten: Int) {
        pocetSten = aPocetSten
    }

    func vratPocetSten() -> Int {
        return pocetSten
    }
}

výstup:

10

Všetko funguje, ako sme očakávali. Swift nám už v tejto chvíli nevygeneruje prázdny (tzv. Bezparametrický konštruktor), takže kocku bez parametra vytvoriť nedá. My to však môžeme umožniť, vytvorme si ďalší konštruktor a tentoraz bez parametra. V ňom nastavíme počet stien na 6, pretože takú hodnotu asi užívateľ našej triedy u kocky očakáva ako predvolený:

init() {
    pocetSten = 6
}

Skúsme si teraz vytvoriť 2 inštancie kocky, každú iným konstruktoru (v main.swift):

let sestistenna = Kostka()
let desetistenna = Kostka(aPocetSten: 10)
print(sestistenna.vratPocetSten())
print(desetistenna.vratPocetSten())
class Kostka {
    private var pocetSten : Int

    init() {
        pocetSten = 6
    }

    init(aPocetSten: Int) {
        pocetSten = aPocetSten
    }

    func vratPocetSten() -> Int {
        return pocetSten
    }
}

výstup:

6
10

Swiftu nevadí, že máme 2 metódy s rovnakým názvom, pretože ich parametre sú rôzne. Hovoríme o tom, že metóda init() (teda tu konštruktor) má preťaženia (overload). Toho môžeme využívať aj u všetkých ďalších metód, nielen u konstruktoru. Xcode nám preťaženie pri metódach prehľadne ponúka (vo chvíli, keď za názov metódy napíšeme ľavú zátvorku), variantmi metódy si môžeme listovať pomocou šípok. V ponuke vidíme naše 2 konštruktory:

Napovedanie preťaženie metód v Xcode - Objektovo orientované programovanie vo Swift

Mnoho metód vo Swift má hneď niekoľko preťaženie, skúste sa pozrieť napr. Na metódu append() na String u. Je dobré si u metód prejsť ich preťaženie, aby ste neprogramoval niečo, čo už niekto urobil pred vami.

Ukážeme si ešte, ako ide obísť nepraktický názov atribútu u parametrického konstruktoru (v našom prípade aPocetSten) a potom konštruktory na chvíľu opustíme. Problém je samozrejme v tom, že keď napíšeme:

init(pocetSten: Int) {
    pocetSten = pocetSten
}

Swift nevie, ktorú z premenných myslíme, či parameter alebo atribút. V tomto prípade priraďujeme do parametra znova ten istý parameter. Xcode nás na túto skutočnosť dokonca upozorní. Vnútri triedy sa máme možnosť odkazovať na jej inštanciu, je uložená v premennej self. Využitie si môžeme predstaviť napr. Keby kocka mala metódu dejHraci(hrac: Hrac) a tam by volala hrac.seberKostku(self). Tu by sme hráči pomocou self odovzdali seba samého, teda tú konkrétnu kocku, s ktorou pracujeme. My sa tým tu nebudeme zaťažovať, ale využijeme odkazu na inštanciu pri nastavovaní atribútu:

init(pocetSten: Int) {
    self.pocetSten = pocetSten
}

Pomocou self sme špecifikovali, že ľavá premenná pocetSten náleží inštanciu, pravú Swift chápe ako z parametra. Máme teda 2 konštruktory, ktoré nám umožňujú tvoriť rôzne hracie kocky. Prejdime ďalej.

Náhodné čísla

Definujme na kocke metódu hod(), ktorá nám vráti náhodné číslo od 1 do počtu stien. Je to veľmi jednoduché, metóda bude internal (pôjde volať zvonku) a nebude mať žiadny parameter. Návratová hodnota bude typu Int. Swift nám ponúka pekné metódy random() na číslach a dokonca aj na type bool (kde dostaneme náhodne true alebo false). My chceme celé čísla, takže zavoláme metódu random() na type Int nasledovne: Int.random(in: 1...pocetSten).

Metóda hod() teda bude vyzerať nasledovne:

func hod() -> Int {
    return Int.random(in: 1...pocetSten)
}

Výpis informácií o inštanciu triedy Kostka

Kocka je takmer hotová, ukážme si ešte jednu užitočnú vlastnosť, ktorú ju pridáme a ktorú budeme hojne používať aj vo väčšine našich ďalších objektov. Swift využíva vlastnosť description a je určená na to, aby vrátila tzv. Textovú reprezentáciu inštancie. Hodí sa vo všetkých prípadoch, kedy si inštanciu potrebujeme vypísať alebo s ňou pracovať ako s textom. Túto vlastnosť majú napr. Aj čísla a využíva ju Swift, keď tieto premenné použijeme v interpolovanom String u alebo v metóde print(). Ak si robíme vlastný triedu, mali by sme zvážiť, či sa nám takáto vlastnosť nehodí. Nikdy by sme si nemali robiť vlastnú metódu, napr. Niečo ako vypis(), keď máme vo Swiftu pripravenú cestu, ako toto riešiť. U kocky nemá description vyšší zmysel, ale u bojovníka bude určite vracať jeho meno. My si ju ku kocke rovnako pridáme, bude vypisovať, že sa jedná o kocku a vráti aj počet stien. Najprv si skúsme vypísať našu inštanciu kocky:

let sestistenna = Kostka()
let desetistenna = Kostka(pocetSten: 10)

print(sestistenna)
class Kostka {
    private var pocetSten : Int

    init() {
        pocetSten = 6
    }

    init(pocetSten: Int) {
        self.pocetSten = pocetSten
    }

    func vratPocetSten() -> Int {
        return pocetSten
    }

    func hod() -> Int {
        return Int(arc4random_uniform(UInt32(pocetSten))) + 1
    }
}

Do konzoly sa vypíše iba cesta k našej triede, teda Arena.Kostka. Nižšie si ukážeme ako vlastnosť description definovať. Kód je pre nás zatiaľ komplexné, takže ho moc neriešte, techniky budú vysvetlené v ďalších lekciách :-)

class Kostka : CustomStringConvertible {

    var description: String {
        return "Kostka s \(pocetSten) stěnami"
    }

    // ... zbytek třídy Kostka
let sestistenna = Kostka()
let desetistenna = Kostka(pocetSten: 10)

print(sestistenna)

description som implementovali ako špeciálnu vlastnosť, ktorá umožňuje iba čítanie. V definícii triedy môžete vidieť pridané : CustomStringConvertible - jedná sa o protokol, ktorý špecifikuje, čo trieda vie. Protokoly si vysvetlíme v jednej z nasledujúcich lekcií.

Teraz opäť skúsime do konzoly vypísať priamo inštanciu kocky.

výstup:

Kostka s 6 stěnami

Ešte si naše kocky vyskúšame. Skúsime si v programe s našimi dvoma kockami v cykloch hádzať a pozrieme sa, či fungujú tak, ako sa očakáva:

let sestistenna = Kostka()
let desetistenna = Kostka(pocetSten: 10)

// hod šestistěnnou
print(sestistenna)
for _ in 1...10 {
    print(sestistenna.hod(), terminator: " ")
}

// hod desetistěnnou
print("\n\n \(desetistenna)")
for _ in 1...10 {
    print(desetistenna.hod(), terminator: " ")
}
class Kostka : CustomStringConvertible {

    var description: String {
        return "Kostka s \(pocetSten) stěnami"
    }

    private var pocetSten : Int

    init() {
        pocetSten = 6
    }

    init(pocetSten: Int) {
        self.pocetSten = pocetSten
    }

    func vratPocetSten() -> Int {
        return pocetSten
    }

    func hod() -> Int {
        return Int(arc4random_uniform(UInt32(pocetSten))) + 1
    }
}

Výstup môže vyzerať nejako takto:

Kostka s 6 stěnami
3 6 6 1 6 3 6 2 6 3

Kostka s 10 stěnami
5 9 9 2 10 4 9 3 10 5

Máme hotovú pomerne peknú a nastaviteľnou triedu, ktorá reprezentuje hraciu kocku. Bude sa nám hodiť v našej aréne, ale môžete ju použiť aj kdekoľvek inde. Vidíme, ako OOP umožňuje znovupoužívat komponenty. V budúcej lekcii, Riešené úlohy k 3. lekcii OOP vo Swift , si povieme niečo o odlišnostiach medzi referenčnými dátovými typmi (objekty) a typy hodnotovými (napr. Int). :)

V nasledujúcom cvičení, Riešené úlohy k 3. lekcii OOP vo Swift, si precvičíme nadobudnuté skúsenosti z predchádzajúcich lekcií.


 

Stiahnuť

Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami

Stiahnuté 17x (21.09 kB)

 

Predchádzajúci článok
Riešené úlohy k 1.-2. lekciu OOP vo Swift
Všetky články v sekcii
Objektovo orientované programovanie vo Swift
Preskočiť článok
(neodporúčame)
Riešené úlohy k 3. lekcii OOP vo Swift
Článok pre vás napísal Filip Němeček
Avatar
Užívateľské hodnotenie:
Ešte nikto nehodnotil, buď prvý!
Autor se věnuje vývoji iOS aplikací (občas macOS)
Aktivity