IT rekvalifikace s garancí práce. Seniorní programátoři vydělávají až 160 000 Kč/měsíc a rekvalifikace je prvním krokem. Zjisti, jak na to!
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

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

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

V minulej lekcii, Riešené úlohy k 1.-2. lekciu OOP v Dart , 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 Dart 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ú konzolovú 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 Dart knižnica dart:math, ktorá na tieto účely obsahuje triedu Random. Naša trieda bude mať teraz 2 vlastnosti:

  • pocetSten typu int
  • random typu Random, kde bude náhodný generátor

Minule sme kvôli jednoduchosti nastavovali všetky vlastnosti našej triedy ako verejne prístupné (public) tým, že sme pred nich nepísali podčiarkovník. Väčšinou sa však skôr nechce, aby sa dali zvonku modifikovať (private), čo nastavíme pridaním podčiarknutia ako prvý znak názvu. Vlastnosť je potom viditeľná len vnútri triedy a zvonku sa Dart 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ť, použijeme public. Naša trieda teraz vyzerá asi takto:

import 'dart:math';

class Kostka {
    /// Generátor náhodných čísel
    Random _random;

    /// Počet stěn kostky
    int _pocetSten;
}

Konštruktory

Až doteraz sme nevedeli zvonku nastaviť iné vlastnosti ako public, pretože 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.dart vytvorili takto:

Kostka kostka = new Kostka();

Práve Kostka() je konštruktor. Pretože v našej triede žiadny nie je, Dart 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 musia mať rovnaké meno ako je meno triedy, v našom prípade teda Kostka. V konstruktoru nastavíme počet stien na pevnú hodnotu a vytvoríme inštanciu triedy Random. Konštruktor bude vyzerať nasledovne:

Kostka() {
    _pocetSten = 6;
    _random = new Random();
}

Ak kocku teraz vytvoríme, bude mať vo vlastnosti _pocetSten hodnotu 6 a v _random bude vytvorená inštancia generátora náhodných čísel. Vypíšme si počet stien do konzoly, nech vidíme, že tam hodnota naozaj je. Nie je dobré vlastnosť nastaviť na public, 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 možno ho len čítať metódou, zmeniť ho nemožno). Dart má na tento účel ešte ďalšie konštrukcie, ale tým sa zatiaľ nebudeme zaoberať. Nová metóda bude vyzerať asi takto:

/// Vrátí počet stěn hrací kostky.
int vratPocetSten() {
    return _pocetSten;
}

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

Kostka kostka = new Kostka(); // v tuto chvíli se zavolá konstruktor
print(kostka.vratPocetSten());

Výstup programu:

Konzolová aplikácia
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 kostruktoru parameter:

Kostka(int pocetSten) {
    _pocetSten = pocetSten;
    _random = new Random();
}

Vráťme sa k main.dart a zadajte tento parameter do konstruktoru:

Kostka kostka = new Kostka(10); // v tuto chvíli se zavolá konstruktor s par. 10
print(kostka.vratPocetSten());

Výstup programu:

Konzolová aplikácia
10

Všetko funguje, ako sme očakávali. Dart 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ť a to hneď 2 spôsobmi. Buď si vytvoríme ďalšiu konštruktor, alebo nastavíme predvolenú hodnotu parametra kontruktoru.

V Dart nemôžeme vytvoriť viac konštruktor líšiace sa parametre, ale môžeme vytvoriť tzv. Pomenované konštruktory, ktoré spoznáte tak, že v názve konštruktor je za bodkou nejaký ďalší identifikátor. Vytvoríme si teda pomenovaný konštruktor, ktorý nám vytvorí šesťstenná kocku:

Kostka(int pocetSten) {
    _pocetSten = pocetSten;
    _random = new Random();
}

Kostka.sestistenna() {
    _pocetSten = 6;
    _random = new Random();
}

Druhou možnosťou je ponechať si iba jeden konštruktor, avšak parametra nastaviť predvolenú hodnotu (a tým pádom aj učiniť parameter nepovinný). Nepovinný parameter zabalíme do hranatých zátvoriek [ ] a môžeme mu priradiť predvolenú hodnotu operátorom =. Nepovinné parametre fungujú aj pre metódy a musia byť umiestnené vždy až po povinných parametroch, tj. Napr. void mojeMetoda(int a, int b, [int c = 10]).

Kostka([int pocetSten = 6]) {
    _pocetSten = pocetSten;
    _random = new Random();
}

Do programu si necháme ako pomenovaný konštruktor tak konštruktor s nepovinným parametrom a skúsime si v main.dart vytvoriť 3 inštancie kocky, každú iným konštruktor:

Kostka sestistenna = new Kostka();
Kostka sestistenna2 = new Kostka.sestistenna();
Kostka desetistenna = new Kostka(10);
print(sestistenna.vratPocetSten());
print(sestistenna2.vratPocetSten());
print(desetistenna.vratPocetSten());

Výstup programu:

Konzolová aplikácia
6
6
10

Zapamätajme si, že Dart teda vadí, ak máme 2 metódy (alebo 2 konštruktory) s rovnakým názvom, aj keď ich parametre sú rôzne. U Konštruktor sa s problémom vysporiadame pomocou pomenovaného konstruktoru alebo nepovinných parametrov, pri metódach pomocou nepovinných parametrov alebo zvolením iného názvu metódy.

Ak by sme mali rovnaký názov parametra, ako je názov vlastnosti triedy, nastal by pri priraďovaní problém. Fiktívne si teda urobíme triedu Testovac, ktorá bude mať vlastnosť hodnota typu int a túto vlastnosť sa budeme snažiť v konstruktoru inicializovať. Parameter sa bude logicky volať rovnako, pretože značí hodnotu a tak by sme s našimi znalosťami pravdepodobne napísali niečo takéto:

class Testovac {
    int hodnota;

    Testovac(int hodnota) {
        hodnota = hodnota;
    }
}

Čo však nebude fungovať, pretože obaja výskyty hodnota v konstruktoru sú zviazané s parametrom hodnota, nie s vlastnosťou hodnota. V tomto prípade musíme pre odlíšenie vlastnosti od parametra použiť kľúčové slovo this, ktoré značí odkaz na seba samého, čím Dart povieme, že chceme pracovať s vlastnosťou, nie s parametrom:

class Testovac {
    int hodnota;

    Testovac(int hodnota) {
        this.hodnota = hodnota;
    }
}

Podobne by sme postupovali aj u funkcií, ktoré by pracovali s vlastnosťami. Aby sme sa v konstruktoru neupisali, ponúka Dart skvelú syntaktickou skratku:

class Testovac {
    int hodnota;

    Testovac(this.hodnota);
}

Namiesto definovanie parametra a následne uloženie parametra do vlastnosti v telu konstruktoru stačí len uviesť vlastnosť objektu s kľúčovým slovom this. Konštruktor je v tomto prípade vygenerovaný automaticky.

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 public (pôjde volať zvonku) a nebude mať žiadny parameter. Návratová hodnota bude typu int. Náhodné číslo získame tak, že na generátore zavoláme metódu nextInt(). Tá prijíma jeden parameter, značiaci maximálnu hodnotu, ktorá nie je zahrnutá. Minimálna hodnota je 0. Inými slovami, metóda vráti číslo 0 (vrátane) až max (bez).

Ak potrebujeme nastaviť inú minimálnu hodnotu, jednoducho k výsledku pripočítame číslo, ktoré sa má stať minimom. Pozor však na to, že týmto posunieme aj maximum, tj. V určitých momentoch by sme museli zmenšiť maximum. Nám ale posun minima aj maxima o 1 vyhovuje, pretože týmto postupom dostaneme rozsah 1 (vrátane) až max (vrátane).

/// Vykoná hod kostkou.
///
/// Vrací číslo od 1 do počtu stěn (včetně).
int hod() {
    return _random.nextInt(_pocetSten) + 1;
}

Prekrývanie metódy toString ()

Kocka je takmer hotová, ukážme si ešte jednu užitočnú metódu, ktorú kocke pridáme a ktorú budeme hojne používať aj vo väčšine našich ďalších objektov. Reč je o metóde toString(), o ktorej sme sa už zmienili a ktorú obsahuje každý objekt, teda aj teraz naše kocka. Metóda 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 metódu majú napr. Aj čísla. Už vieme, že v Dart funguje implicitné konverzie, akonáhle teda budeme chcieť do konzoly vypísať číslo alebo ktorýkoľvek iný objekt, Dart na ňom zavolá metódu toString() a vypíše jej výstup. Ak si robíme vlastný triedu, mali by sme zvážiť, či sa nám takáto metóda nehodí. Nikdy by sme si nemali robiť vlastnú metódu, napr. Niečo ako vypis(), keď máme v Dart pripravenú cestu, ako toto riešiť. U kocky nemá toString() 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ť do konzoly našu inštanciu kocky:

print(sestistenna);

Výstup programu:

Konzolová aplikácia
Instance of 'Kostka'

Do konzoly sa vypíše iba názov našej triedy, teda Kostka. Metódu nemôžeme len tak definovať, pretože je už definovaná (v ďalších dieloch zistíme prečo). Musíme ju teda prepísať, resp. prekryť. Tým sa opäť nebudeme teraz podrobne zaoberať, však chcem, aby sme už teraz vedeli toString() používať. K prekrytie použijeme kľúčové slovo override:

/// Vrací textovou reprezentaci kostky.
@override
String toString() {
    return 'Kostka s $_pocetSten stěnami';
}

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

Výstup programu:

Konzolová aplikácia
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:

// vytvoření
Kostka sestistenna = new Kostka();
Kostka desetistenna = new Kostka(10);

// hod šestistěnnou
print(sestistenna);
for (int i = 0; i < 10; i++)
    stdout.write('${sestistenna.hod()} ');

// hod desetistěnnou
print('\n\n$desetistenna');
for (int i = 0; i < 10; i++)
    stdout.write('${desetistenna.hod()} ');

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

Konzolová aplikácia
Kostka s 6 stěnami
6 5 5 6 5 4 2 6 4 2

Kostka s 10 stěnami
1 6 3 4 6 8 1 9 10 8

Máme hotovú celkom 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 v Dart , 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 v Dart, si precvičíme nadobudnuté skúsenosti z predchádzajúcich lekcií.


 

Mal si s čímkoľvek problém? Stiahni si vzorovú aplikáciu nižšie a porovnaj ju so svojím projektom, chybu tak ľahko nájdeš.

Stiahnuť

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

Stiahnuté 11x (2.38 kB)
Aplikácia je vrátane zdrojových kódov v jazyku Dart

 

Predchádzajúci článok
Riešené úlohy k 1.-2. lekciu OOP v Dart
Všetky články v sekcii
Objektovo orientované programovanie v Dart
Preskočiť článok
(neodporúčame)
Riešené úlohy k 3. lekcii OOP v Dart
Článok pre vás napísal Honza Bittner
Avatar
Užívateľské hodnotenie:
Ešte nikto nehodnotil, buď prvý!
FIT ČVUT alumnus :-) Sleduj mě na https://twitter.com/tenhobi a ptej se na cokoli na https://github.com/tenhobi/ama.
Aktivity