Príprava grafického projektu 2

V ďalšej prípravnej fáze urobíme predbežný návrh hierarchie tried, ktoré budeme v rámci projektu definovať, určíme si východiská a premyslíme postup.

Stabilným bodom bude pre nás trieda GRobot. Od nej budeme odvodzovať všetky triedy, od ktorých očakávame prácu s grafikou alebo inými prvkami sveta grafického robota. Počas tvorby textovej verzie hry sme získali predstavu prinajmenšom o tom, aké figúrky by mali hru tvoriť a ako by sa mali správať. V grafickej verzii budeme očakávať, aby sa figúrky pohybovali a vydávali zvuky. (To ich predurčuje byť potomkami triedy GRobot.)

(Súbor: environment-draft.png)

Povedzme, že z hľadiska funkčnosti a princípov, ktoré bude potrebné pre jednotlivé figúrky naprogramovať, budeme odlišovať pasažierov od prievozníka. Pre prievozníka bude stačiť, ak sa bude po obrazovke kĺzať. Animácia kráčania pasažierov bude postačovať aj veľmi jednoduchá (tú sme v podstate určili fázami pohybu na obrázkoch vytvorených v predchádzajúcej prípravnej kapitole). Z pohľadu pohybu pasažierov bude potrebné zabezpečiť, aby sa figúrky boli schopné (za súčasnej animácie kráčania) dostať z brehu na plť a späť (platí pre oba brehy), a zároveň, aby sa počas prítomnosti na plti pohybovali súčasne s ňou. Prostredie hry (brehy a rieka) môže byť nakreslené ako statické pozadie. Pokiaľ ide o ostatné princípy, budeme sa inšpirovať i textovou verziou hry.

Dá sa očakávať, že všetky figúrky budú mať určitú množinu vlastností spoločnú a že budú jestvovať podmnožiny rozdielov pre pasažierov a prievozníka. Pri textovej verzii nám stačilo pamätať si jediný údaj vyjadrujúci na ktorom brehu sa figúrka nachádza. V grafickom režime musíme počítať s omnoho väčším objemom údajov pre každú postavičku. Aktuálna poloha objektu na obrazovke, rýchlosť pohybu, cieľová poloha, fáza animácie kráčania… to všetko sú informácie, ktoré musia byť niekde uchované. Mnohé z nich dokážeme pokryť funkčnosťou triedy grafického robota, určite však budeme musieť vytvárať niektoré vlastné.

Objektovo orientovaný prístup v programovaní nám dovoľuje oddeliť spoločné vlastnosti figúrok do samostatnej nadtriedy a rozdiely upraviť v odvodených triedach. Vytvoríme preto hierarchiu tried, kde nadtrieda bude priamym potomkom triedy GRobot a z nej budú odvodené triedy určujúce samostatné správanie pre prievozníka a pasažierov. Hierarchiu ilustruje nasledujúci obrázok:

(Súbor: class-hierarchy-01.png)

Zvoľme si pre nich názvy. Triedu určujúcu spoločné prvky postavičiek nazvime AnimovanýObjekt. Bude uchovávať informácie o „kotviacich“ bodoch postavičky na jednotlivých brehoch, o súboroch (grafických a zvukových), ktoré postavička používa, rôzne pomocné atribúty (napríklad pre animáciu, zobrazenie popisu nad postavičkou) a iné. Triedu prievozníka nazvime Prievozník a triedu pre pasažierov nazvime Pasažier.

AnimovanýObjekt bude zhŕňať všetko, čo bude spoločné pre všetky animované objekty v hre (vlk, koza, kapusta a prievozník). Rozdiely v správaní zapracujeme do odvodených tried. Pri kategorizácii všetkých objektov hry, by sme zistili, že môžeme vytvoriť dve kategórie animovaných objektov (pričom jedna z nich by obsahovala jeden jediný prvok). Takže postačí naprogramovať dve triedy odzrkadľujúce odlišnosti v správaní – PrievozníkPasažier. Toto je princíp objektovej dedičnosti (inheritance). Z týchto tried následne vytvoríme konkrétne inštancie (objekty). Z triedy Prievozník vytvoríme jedinú inštanciu a z triedy Pasažier tri.

Okrem týchto tried bude v projekte určite figurovať hlavná trieda (označená identifikátorom HlavnáTrieda), v ktorej vytvoríme a prepojíme inštancie postavičiek a vymenovacia trieda (enumerácia – trieda typu enum), pomocou ktorej budeme vyjadrovať rôzne stavy animovaných objektov.

Vymenovacie triedy v Jave slúžia, okrem iného, na definovanie rôznych stavov objektov. Majú širokú funkcionalitu, ale nám bude stačiť použitie ich základného (najjednoduchšieho) tvaru. Definujeme vymenovaciu triedu pomocou ktorej budeme vyjadrovať logickú pozíciu postavičky. Nazveme ju Pozícia.

Definícia triedy je jednoduchá – niekoľkoriadková. Pri vytváraní novej triedy zvolíme „Vymenovací typ“ a hodnoty vytvorené šablónou (dni v týždni) nahradíme vlastnými hodnotami. Zdrojový kód vymenovacej triedy Pozícia bude vyzerať takto:

public enum Pozícia
{
    pravýBreh, ľavýBreh, prievozník
}

S jej pomocou budeme schopní vyjadriť tri hraničné situácie, v ktorých sa môže postavička nachádzať: prítomnosť na niektorom z brehov alebo prítomnosť „na“ prievozníkovi (na jeho plti, pričom tento stav nemá zmysel pre samotného prievozníka). Takže pri grafickom projekte nestačí vyjadriť pozíciu premennou typu boolean (pravý breh/ľavý breh). Potrebujeme ešte jeden „medzistav“ na vyjadrenie situácie, keď sa postavička nachádza „na“ prievozníkovi.

Na záver si vysvetlime programátorskú techniku, ktorú budeme často používať v nasledujúcich kapitolách. K jej vysvetleniu sa nebudeme vracať, preto si ju dobre zapamätajte! Ide o takzvané prekrývanie metód (niekedy nazývané „prepisovanie“ metód). Dotýka sa to objektového polymorfizmu, čiže „viactvarosti“ objektov (polymorphism). Prekrývanie metód je technika, pri ktorej v odvodenej triede napíšeme nové telo metódy definovanej predtým v rodičovskej triede – prekryjeme ju. To má za následok to, že keď metódu zavoláme pre inštanciu rodičovskej triedy, zavoláme originálnu metódu, no keď ju zavoláme pre inštanciu odvodenej triedy, bude volaná nová verzia metódy, ktorou sme prekryli jej rovnomennú predchodkyňu.

Vysvetlíme si to názorne na situácii úzko súvisiacej s naším projektom. Skupina tried GRobot má preddefinovaných množstvo stavov a (niekedy prázdnych) metód, ktoré môžu odvodené triedy využívať podľa uváženia. Okrem bežných stavov a vlastností typu poloha, orientácia (smer), rýchlosť, farba, hrúbka čiary a podobne, sú tu aj niektoré metódy čakajúce na prekrytie. Predvolene sú prázdne – nerobia nič. Ak ich odvodená trieda prekryje, zmení správanie robota, čím získa kontrolu nad určitými záležitosťami súvisiacimi so správaním a fungovaním robota. Typickým príkladom je prekrytie metódy aktivita:

@Override public void aktivita()
{
}

Získame tým kontrolu nad správaním aktívneho robota. Čokoľvek napíšeme do prekrytej (symbolizuje to klauzula @Override) metódy, bude vykonané pre aktívne inštancie odvodenej triedy. Klauzula @Override síce nie je (z pohľadu syntaxe Javy) povinná, ale ak ju neuvedieme a urobíme nechcený preklep v hlavičke metódy, program nebude správne fungovať napriek tomu, že bol preložený bez chýb. Dôvodom je, že nedôjde ku korektnému prekrytiu želanej metódy. Také chyby sa ťažko hľadajú, preto budeme z pohľadu programátorskej korektnosti považovať túto klauzulu za povinnú!

Dôležité upozornenie! Na to, aby sme v novej vytváranej triede mohli používať vlastnosti robota a prekrývať jeho metódy, musí byť trieda odvodená od triedy GRobot! Takže ak napríklad definujeme novú triedu AnimovanýObjekt, v ktorej prekrývame metódu aktivita, musíme vždy skontrolovať, či je trieda skutočne odvodená od triedy GRobot (buď vizuálne – prítomnosť šípky v diagrame hlavného okna BlueJ, alebo kontrolou zápisu v zdrojovom kóde). V programe (zdrojovom kóde triedy) sa táto skutočnosť prejaví zápisom extends GRobot pri hlavičke triedy:

public class AnimovanýObjekt extends GRobot

Vymenovacie typy vytvorené v rámci tejto kapitoly sú príliš jednoduché na to, aby bolo nutné ich pripájať v samostatných prílohách tejto kapitoly.