3. diel - Hracia kocka v C # - Konštruktory a náhodné čísla
V predchádzajúcom cvičení, Riešené úlohy k 1.-2. lekciu OOP v C # .NET, 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 C # .NET , sme si naprogramovali prvú objektovú aplikáciu. Už vieme tvoriť nové triedy a vkladať do nich atribúty a metódy s parametrami a návratovú hodnotou. V dnešnom C # .NET 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 atribúty, 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 .NET framework, ktorý na tieto účely
obsahuje triedu Random
. Naša trieda bude mať teraz 2
atribúty:
pocetSten
typuint
random
typuRandom
, kde bude náhodný generátor.Minule sme kvôli jednoduchosti nastavovali všetky atribúty 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átorprivate
. Atribút je potom viditeľný len vnútri triedy a zvonku sa C # tvári, že vôbec neexistuje. Pri návrhu triedy teda nastavíme všetko naprivate
a v prípade, že niečo bude naozaj potrebné vystaviť, použijemepublic
. Naša trieda teraz vyzerá asi takto:
/// <summary> /// Trieda reprezentuje hraciu kocku /// </summary> class Kostka { /// <summary> /// Generátor náhodných čísel /// </summary> private Random random; /// <summary> /// Počet stien kocky /// </summary> private int pocetSten; }
Konštruktory
Až doteraz sme nevedeli zvonku nastaviť iné atribúty 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
Program.cs
vytvorili takto:
Kostka kostka = new Kostka();
Práve Kostka()
je konštruktor. Pretože v našej triede
žiadny nie je, C # 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:
public Kostka() { pocetSten = 6; random = new Random(); }
Ak kocku teraz vytvoríme, bude mať v atribúte pocetSten
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é atribút 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 atribútu pocetSten
. Docielili sme tým
v podstate toho, že je atribút read-only (atribút nie je viditeľný a možno
ho len čítať metódou, zmeniť ho nemožno). C # má na tento účel ešte
ďalšie konštrukcie, ale tým sa zatiaľ nebudeme zaoberať. Nová metóda
bude vyzerať asi takto:
/// <summary> /// Vráti počet stien hracie kocky /// </summary> /// <returns>počet stien hracie kocky</returns> public int VratPocetSten() { return pocetSten; }
Presuňme sa do Program.cs
a vyskúšajme si vytvoriť kocku a
vypísať počet stien:
{CSHARP_CONSOLE} Kostka kostka = new Kostka(); // v túto chvíľu sa zavolá konštruktor Console.WriteLine(kostka.VratPocetSten()); Console.ReadKey(); {/CSHARP_CONSOLE}
{CSHARP_OOP} class Kostka { private Random random; private int pocetSten; public Kostka() { pocetSten = 6; random = new Random(); } public int VratPocetSten() { return pocetSten; } } {/CSHARP_OOP}
výstup:
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:
public Kostka(int aPocetSten) { pocetSten = aPocetSten; random = new Random(); }
Všimnite si, že sme pred názov parametra metódy pridali znak "a",
pretože inak by mal rovnaký názov ako atribút a C # by to zmiatlo. Vráťme
sa k Program.cs
a zadajte tento parameter do konstruktoru:
{CSHARP_CONSOLE} Kostka kostka = new Kostka(10); // v túto chvíľu sa zavolá konštruktor s par. 10 Console.WriteLine(kostka.VratPocetSten()); Console.ReadKey(); {/CSHARP_CONSOLE}
{CSHARP_OOP} class Kostka { private Random random; private int pocetSten; public Kostka(int aPocetSten) { pocetSten = aPocetSten; random = new Random(); } public int VratPocetSten() { return pocetSten; } } {/CSHARP_OOP}
výstup:
Konzolová aplikácia
10
Všetko funguje, ako sme očakávali. C # 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ý:
public Kostka() { pocetSten = 6; random = new Random(); }
Skúsme si teraz vytvoriť 2 inštancie kocky, každú iným konstruktoru (v
Program.cs
):
{CSHARP_CONSOLE} Kostka sestistenna = new Kostka(); Kostka desetistenna = new Kostka(10); Console.WriteLine(sestistenna.VratPocetSten()); Console.WriteLine(desetistenna.VratPocetSten()); Console.ReadKey(); {/CSHARP_CONSOLE}
{CSHARP_OOP} class Kostka { private Random random; private int pocetSten; public Kostka() { pocetSten = 6; random = new Random(); } public Kostka(int aPocetSten) { pocetSten = aPocetSten; random = new Random(); } public int VratPocetSten() { return pocetSten; } } {/CSHARP_OOP}
výstup:
Konzolová aplikácia
6
10
C # 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 Kostka()
(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. Visual Studio 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. Tohto
pomocníka nazval MS IntelliSense. V ponuke vidíme naše 2 konštruktory:
Mnoho metód v .NET má hneď niekoľko preťaženie, skúste sa pozrieť
napr. Na metódu Remove()
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. Napríklad metóda WriteLine()
, ktorú poznáte
pre vypisovanie do konzoly, má hneď 18 variantov
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:
public Kostka(int pocetSten) { pocetSten = pocetSten; random = new Random(); }
C # nevie, ktorú z premenných myslíme, či parameter alebo atribút. V
tomto prípade priraďujeme do parametra znova ten istý parameter. VS 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
this
. Využitie si môžeme predstaviť napr.
Keby kocka mala metódu DejHraci(Hrac hrac)
a tam by volala
hrac.SeberKostku(this)
. Tu by sme hráči pomocou
this
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:
public Kostka(int pocetSten) { this.pocetSten = pocetSten; random = new Random(); }
Pomocou this
sme špecifikovali, že ľavá premenná
pocetSten
náleží inštanciu, pravú C # 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
Definujeme 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 Next()
. Tá má niekoľko
preťaženie:
Next()
: Variant bez parametra vracia náhodné číslo v celom rozsahu dátového typuint
;Next(do)
: Vracia nezáporné čísla menšie ako medzado
. Napr.random.Next(100)
teda vráti číslo od0
do99
.Next(od, do)
: Vráti náhodné číslo v zadanej medzi, pričomod
do intervalu patrí ado
už nie. Teda náhodné číslo od1
do100
by vrátilorandom.Next(1, 101)
;Pre naše účely sa najlepšie hodia tretej preťaženie, píšeme teda:
/// <summary> /// Vykoná hod kockou /// </summary> /// <returns>Číslo od 1 do počtu stien</returns> public int hod() { return random.Next(1, pocetSten + 1); }
Dajte si pozor, aby ste netvorili generátor náhodných čísel v metóde,
ktorá má náhodné číslo vracať, teda že by sa pre každé náhodné
číslo vytvoril nový generátor. Výsledná čísla potom nie sú takmer
náhodná alebo dokonca vôbec. Vždy si vytvorte jednu zdieľanú inštanciu
generátora (napr. Do privátneho atribútu pomocou konstruktoru) a na tej potom
metódu Next()
volajte.
Prekrývanie metódy toString ()
Kocka je takmer hotová, ukážme si ešte jednu užitočnú metódu, ktorú
ju 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 C # funguje implicitné
konverzie, akonáhle teda budeme chcieť do konzoly vypísať číslo alebo
ktorýkoľvek iný objekt, C # 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 C # 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:
{CSHARP_CONSOLE} Kostka sestistenna = new Kostka(); Kostka desetistenna = new Kostka(10); Console.WriteLine(sestistenna); Console.ReadKey(); {/CSHARP_CONSOLE}
{CSHARP_OOP} class Kostka { private Random random; private int pocetSten; public Kostka() { pocetSten = 6; random = new Random(); } public Kostka(int pocetSten) { this.pocetSten = pocetSten; random = new Random(); } public int VratPocetSten() { return pocetSten; } public int hod() { return random.Next(1, pocetSten + 1); } } {/CSHARP_OOP}
Do konzoly sa vypíše iba cesta k našej triede, teda
Arena.Kostka
. Metódu nemôžeme len tak definovať, pretože je
už definovaná (v ďalších lekciách 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
:
{CSHARP_OOP} class Kostka { private Random random; private int pocetSten; public Kostka() { pocetSten = 6; random = new Random(); } public Kostka(int pocetSten) { this.pocetSten = pocetSten; random = new Random(); } public int VratPocetSten() { return pocetSten; } public int hod() { return random.Next(1, pocetSten + 1); } /// <summary> /// Vracia textovú reprezentáciu kocky /// </summary> /// <returns>Textová reprezentácia kocky</returns> public override string ToString() { return String.Format("Kocka s {0} stenami", pocetSten); } } {/CSHARP_OOP}
{CSHARP_CONSOLE} Kostka sestistenna = new Kostka(); Kostka desetistenna = new Kostka(10); Console.WriteLine(sestistenna); Console.ReadKey(); {/CSHARP_CONSOLE}
Teraz opäť skúsime do konzoly vypísať priamo inštanciu kocky.
výstup:
Konzolová aplikácia
Kocka s 6 stenami
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:
{CSHARP_CONSOLE} // vytvorenie Kostka sestistenna = new Kostka(); Kostka desetistenna = new Kostka(10); // hod šesťstenná Console.WriteLine(sestistenna); for (int i = 0; i < 10; i++) Console.Write(sestistenna.hod() + " "); // hod desetistěnnou Console.WriteLine("\n\n" + desetistenna); for (int i = 0; i < 10; i++) Console.Write(desetistenna.hod() + " "); Console.ReadKey(); // Ak budete spúšťať kód cez náš online kompiler, výsledok // je udržiavaný v cache a budú padať stále tá istá čísla. // S akoukoľvek zmenou v kóde (napr. aj pridanie komentára) vyvoláte // novú kompiláciu a teda aj vygenerovanie nových čísel. {/CSHARP_CONSOLE}
{CSHARP_OOP} class Kostka { private Random random; private int pocetSten; public Kostka() { pocetSten = 6; random = new Random(); } public Kostka(int pocetSten) { this.pocetSten = pocetSten; random = new Random(); } public int VratPocetSten() { return pocetSten; } public int hod() { return random.Next(1, pocetSten + 1); } public override string ToString() { return String.Format("Kocka s {0} stenami", pocetSten); } } {/CSHARP_OOP}
Konzolová aplikácia
Kocka s 6 stenami
3 6 6 1 6 3 6 2 6 3
Kocka s 10 stenami
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 v C # .NET , 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 C # .NET, 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é 3096x (2.54 MB)
Aplikácia je vrátane zdrojových kódov v jazyku C#