9. diel - Dedičnosť a polymorfizmus v JavaScripte
V minulej lekcii, Dokončenie objektového diáre v JavaScripte , sme dokončili náš objektový diár. Dnes sa v JavaScript tutoriálu pozrieme na dedičnosť a polymorfizmus.
Dedičnosť
Dedičnosť je jedna zo základných techník OOP a slúži k tvoreniu
nových dátových štruktúr na základe starých. Poďme si to ukázať na
príklade. Vytvoríme si všeobecnú triedu Clovek
, ktoré
priradíme vlastnosti meno a vek a ešte metódu na predstavenie:
class Clovek { constructor(jmeno, vek) { this.jmeno = jmeno; this.vek = vek; } predstavSe() { return `Jmenuji se ${this.jmeno} a je mi ${this.vek}.`; } }
Kód by vás nemal ničím prekvapiť.
Rozšírenie triedy
Povedzme, že si s touto triedou v aplikácii chvíli vystačíme. Čo keď potom ale potrebujeme ľudí, ktorí budú mať nejaké vlastnosti navyše? Napr. taký programátor je tiež človek s menom, vekom a schopnosťou sa predstaviť, ale navyše by mal programovací jazyk, v ktorom programuje.
Naivné riešenie
Možno by vás napadlo vytvoriť triedu Programator
,
skopírovať do nej kód z triedy Clovek
a pridať len navyše
vlastnosť jazyk
a napr. Tiež metódu
programuj()
:
class Programator { constructor(jmeno, vek, jazyk) { this.jmeno = jmeno; this.vek = vek; this.jazyk = jazyk; } predstavSe() { return `Jmenuji se ${this.jmeno} a je mi ${this.vek}.`; } programuj() { return `Programuji v ${this.jazyk}...`; } }
Toto riešenie je síce funkčný, ale porušuje jednu z najzákladnejších
dobrých praktík všetkých programátorov - Princíp DRY (Do not Repeat
Yourself, teda neopakujte sa). Predstavte si, že sa v budúcnosti rozhodnete
triedu Clovek
upraviť a zabudnete, že je rozkopírovanie do
niekoľkých ďalších súborov. Takéto chyby sa potom zle hľadajú a
samozrejme spôsobí znefunkčnenie vašej aplikácie. Takto teda určite nie
Riešenie pomocou dedičnosti
Novú triedu Programator
naozaj vytvoríme, ale z triedy
Clovek
ju oddědíme. V JavaScriptu sa pre dedenie
používa v definícii novej triedy kľúčové slovo
extends
, takto:
class Potomek extends Predek
Konštruktory a dedičnosť
Je tu však malý háčik. Akonáhle potrebujeme v triede potomka
konštruktor, musíme v ňom najprv zavolať konštruktor predka pomocou funkcie
super()
, prípadne mu odovzdať potrebné parametre. Toto
volanie musíme vykonať pred použitím kľúčového slova this
.
Ono je to aj logické, konštruktor objekt pripravuje na použitie a ak má
predok nejaký konštruktor, potomok by ho mal vo svojom konstruktoru zavolať,
aby všetko Inicializoval. Konštruktor predka musíme zavolať v konstruktoru
potomka aj v prípade, keby predok nemal definovaný svoj vlastný konštruktor
(aj v tomto prípade totiž konštruktor má, len sa mu vygeneroval
automaticky).
Poďme teda od našej triedy Clovek
oddědit novú triedu,
Programator
:
class Programator extends Clovek { constructor(jmeno, vek, jazyk) { super(jmeno, vek); this.jazyk = jazyk; } programuj() { return `Programuji v ${this.jazyk}...`; } }
Ako vidíme, máme tu konštruktor s tromi parametrami, z čoho dva
odovzdávame pri zavolaní super()
konstruktoru triedy
Clovek
, tj. Meno a vek. Ďalej tu máme už spomínanou vlastnosť
jazyk
, ktorú tu vytvoríme v konstruktoru pre programátora a
priradíme do nej hodnotu z rovnomenného parametra. Trieda programátor má
teda teraz tri vlastnosti: jmeno
, vek
a
jazyk
a dve metódy - odděděnou predstavSe()
a
programuj()
.
Teraz si v obsluhe vytvoríme inštanciu triedy Programator
a
vyskúšame si zavolať obe metódy:
const programator = new Programator("Šimon", 19, "JS"); document.write(` ${programator.predstavSe()}<br> ${programator.programuj()}<br> `);
Teraz sa môžeme pozrieť na výsledok:
Výhody dedičnosti
Výhody dedenie sú jasné, nemusíme opisovať obom triedam tie isté vlastnosti, ale stačí dopísať len to, v čom sa líšia. Zvyšok sa zdedí. Prínos je obrovský, môžeme rozširovať existujúce komponenty o nové metódy a tým je znovu využívať. Nemusíme písať hŕbu redundantného (duplikovaného) kódu. A hlavne - keď zmeníme jediný atribút v materskej triede, automaticky sa táto zmena všade zdedí. Nedôjde teda k tomu, že by sme to museli meniť ručne u 20tich tried a niekde na to zabudli a spôsobili chybu. Sme ľudia a chybovať budeme vždy, musíme teda používať také programátorské postupy, aby sme mali možnosť chybovať čo najmenej.
Jazyky, ktoré dedičnosť podporujú, buď vie dedičnosť jednoduchú, kde trieda dedí len z jednej triedy, alebo viacnásobnú, kde trieda dedí hneď z niekoľkých tried naraz. Viacnásobná dedičnosť sa v praxi príliš neosvedčila. JavaScript podporuje len jednoduchú dedičnosť, s viacnásobnou dedičnosťou sa môžete stretnúť napr. V C ++. Potomok samozrejme môže mať ďalšieho potomka a tak ďalej.
Polymorfizmus
Nenechajte sa vystrašiť príšerným názvom tejto techniky, pretože je v
jadre veľmi jednoduchá. Polymorfizmus umožňuje používať jednotné
rozhranie pre prácu s rôznymi typmi objektov. Majme napríklad veľa objektov,
ktoré reprezentujú nejaké geometrické útvary (kruh, štvorec,
trojuholník). Bolo by určite prínosné a prehľadné, keby sme s nimi mohli
komunikovať jednotne, hoci sa líšia. Môžeme zaviesť triedu
GeometrickyUtvar
, ktorá by obsahovala atribút barva
a metódu vykresli()
. Všetky geometrické tvary by potom dedili z
tejto triedy jej interface (rozhranie). Objekty kruh a štvorec sa ale iste
vykresľujú inak. Polymorfizmus nám umožňuje prepísať si metódu
vykresli()
pri každej podtriedy tak, aby robila, čo chceme.
Rozhranie tak zostane zachované a my nebudeme musieť premýšľať, ako sa to
u onoho objekte volá.
Polymorfizmus býva často vysvetľovaný na obrázku so zvieratami, ktoré
majú všetky v rozhraní metódu speak()
, ale každé si ju
vykonáva po svojom:
Podstatou polymorfizmu je teda metóda alebo metódy, ktoré majú všetci potomkovia definované s rovnakou hlavičkou, ale iným telom. Vyskúšajme si to na našich ľuďoch.
Predstavenie sa
Človek má definovanú metódu predstavSe()
, ktorá pozdravia
týmto spôsobom:
Jmenuji se Marek a je mi 26.
Prepísanie metódy
Teraz aplikujeme polymorfizmus tak, že v triede Programator
metódu predstavSe()
z predka upravíme. Prepísanie metódy v
JavaScripte je jednoduché - stačí ju znovu definovať. Naučme teda
programátora predstavovať sa nejakým programátorským spôsobom, napr. Že
použije pozdrav "Hello world!":
class Programator extends Clovek { constructor(jmeno, vek, jazyk) { super(jmeno, vek); this.jazyk = jazyk; } programuj() { return `Programuji v ${this.jazyk}...`; } predstavSe() { return `Hello world! Jmenuji se ${this.jmeno} a je mi ${this.vek}.`; } }
výsledok:
Ak metódu predstavSe()
zavoláte na inštanciu triedy
Clovek
, vypíše pôvodný text pozdravu. Môžete si to skúsiť
Získali sme teda zhodné
rozhrania a inú funkcionalitu podľa konkrétneho objektu.
Vylepšenia
Všimnite si, že sme použili základné predstavenie z triedy
Clovek
, len sme pred neho pridali text "Hello world!". Naše
riešenie vyššie nie je ideálny, pre viac typov ľudí by sme museli stále
kopírovať do každej triedy ten istý predvolený pozdrav a pred neho ešte
len dávať novú hlášku. A už vieme, že podľa DRY je akékoľvek
kopírovanie rovnakého kódu zle.
Základné predstavenie môže vracať metóda predstavSe()
triedy Clovek
a my ju môžeme zavolať z potomkov rovnako ako
predtým konštruktor človeka pomocou kľúčového slova super
.
Jej výstup potom jednoducho pridáme k textovému reťazcu, ktorý sa bude
vracať:
class Programator extends Clovek { constructor(jmeno, vek, jazyk) { super(jmeno, vek); this.jazyk = jazyk; } programuj() { return `Programuji v ${this.jazyk}...`; } predstavSe() { return `Hello world! ${super.predstavSe()}`; } }
Skúsme si vytvoriť v obsluhe inštanciu triedy človek a zavolajte metódu
predstavSe()
, nech vidíme rozdiel:
const programator = new Programator("Šimon", 19, "JS"); document.write(` ${programator.predstavSe()}<br> ${programator.programuj()}<br> `); const clovek = new Clovek("Marek", 26); document.write(clovek.predstavSe());
Výpis bude vyzerať takto:
Tak to je pre dnešok všetko Dole sú opäť k dispozícii zdrojové kódy na stiahnutie. Nabudúce, v lekcii Vlastnosti objektov v JavaScripte , sa pozrieme na vlastnosti v JavaScripte.
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é 152x (1.24 kB)
Aplikácia je vrátane zdrojových kódov v jazyku JavaScript