Body zdarma Body zdarma
Využij podzimních slev a získej od nás až 40 % bodů zdarma! Více zde

Lekce 9 - Dědičnost a polymorfismus v JavaScriptu

Unicorn College Tento obsah je dostupný zdarma v rámci projektu IT lidem.
Vydávání, hosting a aktualizace umožňují jeho sponzoři.

V minulé lekci, Dokončení objektového diáře v JavaScriptu, jsme dokončili náš objektový diář. Dnes se v JavaScript tutoriálu podíváme na dědičnost a polymorfismus.

Dědičnost

Dědičnost je jedna ze základních technik OOP a slouží k tvoření nových datových struktur na základě starých. Pojďme si to ukázat na příkladu. Vytvoříme si obecnou třídu Clovek, které přiřadíme vlastnosti jméno a věk a ještě metodu na představení:

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 neměl ničím překvapit.

Rozšíření třídy

Řekněme, že si s touto třídou v aplikaci chvíli vystačíme. Co když pak ale potřebujeme lidi, kteří budou mít nějaké vlastnosti navíc? Např. takový programátor je také člověk se jménem, věkem a schopností se představit, ale navíc by měl programovací jazyk, ve kterém programuje.

Naivní řešení

Možná by vás napadlo vytvořit třídu Programator, zkopírovat do ní kód z třídy Clovek a přidat jen navíc vlastnost jazyk a např. také metodu 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 řešení je sice funkční, ale porušuje jednu z nejzákladnějších dobrých praktik všech programátorů - Princip DRY (Don't Repeat Yourself, tedy neopakujte se). Představte si, že se v budoucnu rozhodnete třídu Clovek upravit a zapomenete, že je rozkopírovaná do několika dalších souborů. Takové chyby se poté špatně hledají a samozřejmě způsobí znefunkčnění vaší aplikace. Takto tedy určitě ne :)

Řešení pomocí dědičnosti

Novou třídu Programator opravdu vytvoříme, ale z třídy Clovek ji oddědíme. V JavaScriptu se pro dědění používá v definici nové třídy klíčové slovo extends, takto:

class Potomek extends Predek

Konstruktory a dědičnost

Je tu ovšem malý háček. Jakmile potřebujeme ve třídě potomka konstruktor, musíme v něm nejprve zavolat konstruktor předka pomocí funkce super(), případně mu předat potřebné parametry. Toto volání musíme provést před použitím klíčového slova this. Ono je to i logické, konstruktor objekt připravuje na použití a pokud má předek nějaký konstruktor, potomek by jej měl ve svém konstruktoru zavolat, aby vše inicializoval. Konstruktor předka musíme zavolat v konstruktoru potomka i v případě, kdyby předek neměl definovaný svůj vlastní konstruktor (i v tomto případě totiž konstruktor má, jen se mu vygeneroval automaticky).

Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!

Pojďme tedy od naší třídy Clovek oddědit novou třídu, Programator:

class Programator extends Clovek {

    constructor(jmeno, vek, jazyk) {
        super(jmeno, vek);
        this.jazyk = jazyk;
    }

    programuj() {
        return `Programuji v ${this.jazyk}...`;
    }
}

Jak vidíme, máme zde konstruktor se třemi parametry, z čehož dva předáváme při zavolání super() konstruktoru třídy Clovek, tj. jméno a věk. Dále zde máme již zmiňovanou vlastnost jazyk, kterou zde vytvoříme v konstruktoru pro programátora a přiřadíme do ní hodnotu ze stejnojmenného parametru. Třída programátor má tedy nyní tři vlastnosti: jmeno, vek a jazyk a dvě metody – odděděnou predstavSe() a programuj().

Nyní si v obsluze vytvoříme instanci třídy Programator a vyzkoušíme si zavolat obě metody:

const programator = new Programator("Šimon", 19, "JS");
document.write(`
${programator.predstavSe()}<br>
${programator.programuj()}<br>
`);

Nyní se můžeme podívat na výsledek:

Your page
localhost

Výhody dědičnosti

Výhody dědění jsou jasné, nemusíme opisovat oběma třídám ty samé vlastnosti, ale stačí dopsat jen to, v čem se liší. Zbytek se podědí. Přínos je obrovský, můžeme rozšiřovat existující komponenty o nové metody a tím je znovu využívat. Nemusíme psát spousty redundantního (duplikovaného) kódu. A hlavně - když změníme jediný atribut v mateřské třídě, automaticky se tato změna všude podědí. Nedojde tedy k tomu, že bychom to museli měnit ručně u 20ti tříd a někde na to zapomněli a způsobili chybu. Jsme lidé a chybovat budeme vždy, musíme tedy používat takové programátorské postupy, abychom měli možností chybovat co nejméně.

Jazyky, které dědičnost podporují, buď umí dědičnost jednoduchou, kde třída dědí jen z jedné třídy, nebo vícenásobnou, kde třída dědí hned z několika tříd najednou. Vícenásobná dědičnost se v praxi příliš neosvědčila. JavaScript podporuje pouze jednoduchou dědičnost, s vícenásobnou dědičností se můžete setkat např. v C++. Potomek samozřejmě může mít dalšího potomka a tak dále.

Polymorfismus

Nenechte se vystrašit příšerným názvem této techniky, protože je v jádru velmi jednoduchá. Polymorfismus umožňuje používat jednotné rozhraní pro práci s různými typy objektů. Mějme například mnoho objektů, které reprezentují nějaké geometrické útvary (kruh, čtverec, trojúhelník). Bylo by jistě přínosné a přehledné, kdybychom s nimi mohli komunikovat jednotně, ačkoli se liší. Můžeme zavést třídu GeometrickyUtvar, která by obsahovala atribut barva a metodu vykresli(). Všechny geometrické tvary by potom dědily z této třídy její interface (rozhraní). Objekty kruh a čtverec se ale jistě vykreslují jinak. Polymorfismus nám umožňuje přepsat si metodu vykresli() u každé podtřídy tak, aby dělala, co chceme. Rozhraní tak zůstane zachováno a my nebudeme muset přemýšlet, jak se to u onoho objektu volá.

Polymorfismus bývá často vysvětlován na obrázku se zvířaty, která mají všechna v rozhraní metodu speak(), ale každé si ji vykonává po svém:

Polymorfismus v JavaScriptu

Podstatou polymorfismu je tedy metoda nebo metody, které mají všichni potomci definované se stejnou hlavičkou, ale jiným tělem. Vyzkoušejme si to na našich lidech.

Představení se

Člověk má definovanou metodu predstavSe(), která pozdraví tímto způsobem:

Jmenuji se Marek a je mi 26.

Přepsání metody

Nyní aplikujeme polymorfismus tak, že ve třídě Programator metodu predstavSe() z předka upravíme. Přepsání metody v JavaScriptu je jednoduché – stačí ji znovu definovat. Naučme tedy programátora představovat se nějakým programátorským způsobem, např. ž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ýsledek:

Your page
localhost

Pokud metodu predstavSe() zavoláte na instanci třídy Clovek, vypíše původní text pozdravu. Můžete si to zkusit :) Získali jsme tedy shodné rozhraní a jinou funkcionalitu podle konkrétního objektu.

Vylepšení

Všimněte si, že jsme použili základní představení z třídy Clovek, jen jsme před něj přidali text "Hello world!". Naše řešení výše není ideální, pro více typů lidí bychom museli stále kopírovat do každé třídy ten samý výchozí pozdrav a před něj teprve dávat novou hlášku. A již víme, že podle DRY je jakékoli kopírování stejného kódu špatně.

Základní představení může vracet metoda predstavSe() třídy Clovek a my ji můžeme zavolat z potomků stejně jako předtím konstruktor člověka pomocí klíčového slova super. Její výstup pak jednoduše přidáme k textovému řetězci, který se bude vracet:

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()}`;
    }
}

Zkusme si vytvořit v obsluze instanci třídy člověk a zavolejme metodu predstavSe(), ať vidíme rozdíl:

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 vypadat takto:

Your page
localhost

Tak to je pro dnešek vše :) Dole jsou opět k dispozici zdrojové kódy ke stažení. Příště, v lekci Vlastnosti objektů v JavaScriptu, se podíváme na vlastnosti v JavaScriptu.


 

Stáhnout

Staženo 21x (1.24 kB)
Aplikace je včetně zdrojových kódů v jazyce JavaScript

 

 

Aktivity (3)

 

 

Komentáře

Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zatím nikdo nevložil komentář - buď první!