Vydělávej až 160.000 Kč měsíčně! Akreditované rekvalifikační kurzy s garancí práce od 0 Kč. Více informací.
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í.

8. diel - Aréna s mágom (dedičnosť a polymorfizmus)

V minulej lekcii, Dedičnosť a polymorfizmus v Pythone , sme si vysvetlili dedičnosť a polymorfizmus. Na dnešnej Python tutoriál máme sľúbené, že si ich vyskúšame v praxi. Bude to opäť na našej aréne, kde z bojovníka podědíme mága. Tento tutoriál už patrí k tým náročnejším a bude tomu tak aj u ďalších. Preto si priebežne precvičujte prácu s objektmi, skúšajte si naše cvičenia a tiež vymýšľajte nejaké svoje aplikácie, aby ste si zažili základné veci. To, že je tu prítomný celý seriál neznamená, že ho celý naraz prečítate a pochopíte :) Snažte sa programovať priebežne.

mág - Objektovo orientované programovanie v Pythone
Než začneme niečo písať, zhodneme sa na tom, čo by mal mág vedieť. Mág bude fungovať rovnako, ako bojovník. Okrem života bude mať však aj manu. Spočiatku bude mana plná. V prípade plnej many môže mág vykonať magický útok, ktorý bude mať pravdepodobne vyššie damage, ako útok normálne (ale samozrejme záleží na tom, ako si ho nastavíme). Tento útok manu vybije na 0. Každé kolo sa bude mana zvyšovať o 10 a mág bude podnikať len bežný útok. Akonáhle sa mana úplne doplní, opäť bude môcť magický útok použiť. Mana bude zobrazená grafickým ukazovateľom, rovnako ako život.

Vytvoríme teda triedu Mag, zdedíme ju z Bojovnik. Zatiaľ bude vyzerať takto (opäť si ju okomentujte):

class Mag(Bojovnik):

Keďže v mágovi nebudeme mať prístup ku všetkým premenným, pretože sú v bojovníkovi nastavené ako súkromné, tak musíme triedu Bojovnik upraviť. Zmeníme atribúty zo súkromných na vnútorné. Musíme ich ale zmeniť vo všetkých metódach. Budeme potrebovať len kocka a meno, ale pokojne takto nastavíme všetky atribúty charakteru, pretože sa v budúcnosti môžu hodiť, keby sme sa rozhodli zdediť ďalšie typy bojovníkov. Naopak atribút sprava nie je vhodné nastavovať ako vnútorná, pretože nesúvisí s bojovníkom, ale s nejakou vnútornou logikou triedy. Upravený konštruktor bojovníka teda bude vyzerať nejako takto:

class Mag(Bojovnik):

    def __init__(self, jmeno, zivot, utok, obrana, kostka):
        self._jmeno = jmeno
        self._zivot = zivot
        self._max_zivot = zivot
        self._utok = utok
        self._obrana = obrana
        self._kostka = kostka
        self.__zprava = ""

Prejdime ku konstruktoru mága.

Konštruktor potomka

Pôvodný konštruktor bojovníka použiť nemôžeme, lebo máme u mága 2 parametre navyše (mana a magický útok).

Definujeme si teda konštruktor v potomkovi, ktorý berie parametre potrebné pre vytvorenie bojovníka a niekoľko parametrov navyše pre mága.

O potomkov nie je nutné vždy volať konštruktor predka. Náš konštruktor musia mať samozrejme všetky parametre potrebné pre predka plus tie nové, čo má navyše potomok, takže ho volať budeme. Niektoré potom odovzdáme predkovi a niektoré si spracujeme sami. Konštruktor predka sa vykoná pred naším konstruktoru.

V Pythone existuje metóda super (), ktorá zavolá verziu metódy na predkovi. My teda môžeme zavolať konštruktor predka s danými parametrami a potom vykonať navyše inicializáciu pre mága. V Pythone sa volanie konstruktoru predka (metódy super ()) píše do tela metódy.

Konštruktor mága bude teda vyzerať takto:

def __init__(self, jmeno, zivot, utok, obrana, kostka, mana, magicky_utok):
    super().__init__(jmeno, zivot, utok, obrana, kostka)
    self.__mana = mana
    self.__max_mana = mana
    self.__magicky_utok = magicky_utok

Pozn. 1 - V Pythone 2.X by bola syntaxe metódy super () nasledujúce: super (Mag, self) .__ init __ (...) Môžeme ale použiť aj skrátenú verziu pre konštruktor a to: super (...)

Presuňme sa na koniec programu a druhého bojovníka (Shadow) zmeňme na mága, napr. Takto:

gandalf = Mag("Gandalf", 60, 15, 12, kostka, 30, 45)

Zmenu samozrejme musíme urobiť aj v riadku, kde bojovníka do arény vkladáme.

Polymorfizmus a prepisovanie metód

Bolo by výhodné, keby objekt Arena mohol s mágom pracovať rovnako ako s bojovníkom. My už vieme, že takémuto mechanizmu hovoríme polymorfizmus. Aréna zavolá na objekte metódu útočí () so súperom v parametri. Nestará sa o to, či bude útok vykonávať bojovník alebo mág, bude s nimi pracovať rovnako. U mága si teda prepíšeme metódu útočí () z predka. Prepíšeme zdedenú metódu tak, aby útok pracoval s mannou, hlavička metódy však zostane rovnaká.

V potomkovi môžeme jednoducho a bez okolkov prepísať ľubovoľnú metódu. V Pythone sú všetky metódy - povedané terminológiou jazykov C ++, C# - virtuálne.

A keď sme pri metódach, budeme ešte určite používať metódu __nastav_zpravu (), tá je však súkromná. Označme ju ako vnútorné:

def _nastav_zpravu(self, zprava):
    ...

Pri návrhu bojovníka sme samozrejme mali myslieť na to, že sa z neho bude dediť a už označiť vhodné atribúty a metódy ako vnútorné. V tutoriále k bojovníkovi som vás tým však nechcel zbytočne zaťažovať, preto musíme modifikátory zmeniť až teraz, kedy im rozumieme :)

Teraz sa vráťme k metóde útočí () a prepíšeme (prekryjeme) jej. Jej správanie nebude nijako zložité. Podľa hodnoty many buď vykonáme bežný útok alebo útok magický. Hodnotu many potom buď zvýšime o 10 alebo naopak znížime na 0 v prípade magického útoku:

def utoc(self, souper):
    # mana není naplněna
    if self.__mana < self.__max_mana:
        self.__mana = self.__mana + 10
        if self.__mana > self.__max_mana:
            self.__mana = self.__max_mana
        uder = self._utok + self._kostka.hod()
        zprava = "{0} útočí s úderem za {1} hp.".format(self._jmeno, uder)
        self._nastav_zpravu(zprava)
    #magický útok
    else:
        uder = self.__magicky_utok + self._kostka.hod()
        zprava = "{0} použil magii za {1} hp.".format(self._jmeno, uder)
        self._nastav_zpravu(zprava)
        self.__mana = 0
    souper.bran_se(uder)

Kód je asi zrozumiteľný, všimnite si obmedzenia many na max_mana. Môže sa nám totiž stať, že túto hodnotu presiahne, keď ju zvyšujeme o 10. Keď sa nad kódom zamyslíme, tak útok vyššie v podstate vykonáva pôvodnej metóda útočí (). Iste by bolo prínosné zavolať podobu metódy na predkovi namiesto toho, aby sme správanie odpisovali. K tomu opäť použijeme super ():

def utoc(self, souper):
    # mana není naplněna
    if self.__mana < self.__max_mana:
        self.__mana = self.__mana + 10
        if self.__mana > self.__max_mana:
            self.__mana = self.__max_mana
        super().utoc(souper)
    #magický útok
    else:
        uder = self.__magicky_utok + self._kostka.hod()
        zprava = "{0} použil magii za {1} hp.".format(self._jmeno, uder)
        self._nastav_zpravu(zprava)
        self.__mana = 0
        souper.bran_se(uder)

Opäť vidíme, ako môžeme znovupoužívat kód. S dedičnosťou je spojené naozaj mnoho techník, ako si ušetriť prácu. V našom prípade to ušetrí niekoľko riadkov, ale u väčšieho projektu by to mohlo mať obrovský význam. Ostatné nesoukromé metódy sa automaticky zdedí.

Aplikácia teraz funguje tak, ako má.

Ťahová hra aréna s mágom v Pythone - Objektovo orientované programovanie v Pythone

Aréna nás však neinformuje o mane mága, poďme to napraviť. Pridáme mágovi verejnú metódu graficka_mana (), ktorá bude obdobne ako u života vracať reťazec s grafickým ukazovateľom many.

Aby sme nemuseli logiku so zložením ukazovatele písať dvakrát, upravíme metódu graficky_zivot () v triede Bojovnik. Pripomeňme si, ako vyzerá:

def graficky_zivot(self):
    celkem = 20
    pocet = int(self._zivot / self._max_zivot * celkem)
    if (pocet == 0 and self.nazivu):
        pocet = 1
    return "[{0}{1}]".format("#"*pocet, " "*(celkem-pocet))

Vidíme, že nie je výnimkou premenných zivot a max_zivot na živote nijako závislá. Metódu premenujeme na GrafickyUkazatel a dáme ju 2 parametre: aktuálnu hodnotu a maximálnu hodnotu. zivot a max_zivot v tele metódy potom nahradíme za aktualne a maximalnu.

def graficky_ukazatel(self, aktualni, maximalni):
    celkem = 20
    pocet = int(aktualni / maximalni * celkem)
    if (pocet == 0 and self.nazivu):
        pocet = 1
    return "[{0}{1}]".format("#"*pocet, " "*(celkem-pocet))

Metódu graficky_zivot () v triede Bojovnik naimplementujeme znovu, bude nám v nej stačiť jediný riadok a to zavolanie metódy graficky_ukazatel () s príslušnými parametrami:

def graficky_zivot(self):
    return self.graficky_ukazatel(self._zivot, self._max_zivot)

Určite som mohol v tutoriálu s bojovníkom urobiť metódu graficky_ukazatel () rovno. Chcel som však, aby sme si ukázali, ako sa rieši prípady, keď potrebujeme vykonať podobnú funkčnosť viackrát. S takouto parametrizáciou sa praxi budete stretávať často, pretože nikdy presne nevieme, čo budeme v budúcnosti od nášho programu požadovať.

Teraz môžeme vykresľovať ukazovateľ tak, ako sa nám to hodí. Presuňme sa do triedy Mag a naimplementujme metódu graficka_mana ():

def graficka_mana(self):
    return self.graficky_ukazatel(self.__mana, self.__max_mana)

Jednoduché, že? Teraz je mág hotový, zostáva len naučiť arénu zobrazovať manu v prípade, že je bojovník mág. Presuňme sa teda do triedy Arena.

Rozpoznanie typu objektu

Keďže sa nám teraz vykreslenie bojovníka skomplikovalo, urobíme si na neho samostatnú metódu vypis_bojovnika (), jej parametrom bude daná inštancie bojovníka:

def __vypis_bojovnika(self, bojovnik):
    print(bojovnik)
    print("Život: {0}".format(bojovnik.graficky_zivot()))

Teraz poďme reagovať na to, či je bojovník mág. Minule sme si povedali, že k tomu slúži funkcia isinstance:

def __vypis_bojovnika(self, bojovnik):
    print(bojovnik)
    print("Život: {0}".format(bojovnik.graficky_zivot()))
    if isinstance(bojovnik, Mag):
        print("Mana: {0}".format(bojovnik.graficka_mana()))

To by sme mali, vypis_bojovnika () budeme volať v metóde vykresli (), ktorá bude vyzerať takto:

def __vykresli(self):
    self.__vycisti_obrazovku()
    print("-------------- Aréna -------------- \n")
    print("Bojovníci: \n")
    self.__vypis_bojovnika(self.__bojovnik_1)
    self.__vypis_bojovnika(self.__bojovnik_2)
    print("")

Hotovo :)

Ťahová hra aréna s mágom v Pythone - Objektovo orientované programovanie v Pythone

Kód máte v prílohe. Ak ste niečomu nerozumeli, skúste si článok prečítať viackrát alebo pomalšie, sú to dôležité praktiky. V budúcej lekcii, Kvíz - Odkazy na objekty a kopírovanie objektov v Pythone , si vysvetlíme pojem statika.

V nasledujúcom kvíze, Kvíz - Odkazy na objekty a kopírovanie objektov v Pythone, si vyskúšame 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é 1044x (4.42 kB)
Aplikácia je vrátane zdrojových kódov v jazyku Python

 

Predchádzajúci článok
Dedičnosť a polymorfizmus v Pythone
Všetky články v sekcii
Objektovo orientované programovanie v Pythone
Preskočiť článok
(neodporúčame)
Kvíz - Odkazy na objekty a kopírovanie objektov v Pythone
Článok pre vás napísal gcx11
Avatar
Užívateľské hodnotenie:
Ešte nikto nehodnotil, buď prvý!
(^_^)
Aktivity