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
typuint
random
typuRandom
, 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