NOVINKA – Víkendový online kurz Software tester, který tě posune dál. Zjisti, jak na to!
NOVINKA - Online rekvalifikační kurz Java programátor. Oblíbená a studenty ověřená rekvalifikace - nyní i online.

Lekce 8 - Polymorfismus, finální prvky a autoloader v PHP

V minulé lekci, Dědičnost v PHP, jsme si vysvětlili dědičnost a oddědili jsme si od člověka javistu.

Dnes se uvedeme do tzv. polymorfismusmu.

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 - Objektově orientované programování (OOP) v PHP

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.

Pozdrav

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

Pozdrav
localhost

Tuto metodu dědí potomci člověka, tedy i javista, kde se zatím metoda chová stejně. My jsme však schopni zděděnou metodu přepsat tak, aby potomek zdravil jinak, než člověk.

Přepsání metody

Přepsání metody je velmi jednoduché, stačí ji v potomkovi pouze znovu definovat. Naučme tedy javisty zdravit nějakým programátorským způsobem a přidejme metodu pozdrav() do třídy Javista:

public function pozdrav(): void
{
    echo('Hello world! Jsem ' . $this->jmeno);
}

Pojďme si to vyzkoušet do index.php. Abychom plně pocítili výhody polymorfismu, uděláme si několik instancí lidí a javistů a ty si vložíme do pole:

$lide = array();
$lide[] = new Clovek('Karel', 'Novák', 30);
$lide[] = new Javista('Jan', 'Nový', 24, 'Eclipse');
$lide[] = new Clovek('Josef', 'Nový', 50);
$lide[] = new Javista('Tomáš', 'Marný', 28, 'NetBeans');
$lide[] = new Clovek('Marie', 'Nová', 32);

Nyní budeme chtít, aby všichni lidé pozdravili. Jelikož jsme použili polymorfismus, tak na všech instancích zavoláme pozdrav tím samým způsobem. Každá instance potom pozdraví tak, jak to má naprogramované.

foreach ($lide as $clovek) {
    $clovek->pozdrav();
    echo('<br />');
}

class Clovek
{
    private int $unava = 0;

    public function __construct(public string $jmeno, public string $prijmeni, public int $vek) {}

    public function spi(int $doba): void
    {
        $this->unava -= $doba * 10;
        if ($this->unava < 0)
            $this->unava = 0;
    }

    public function behej(int $vzdalenost): void
    {
        if ($this->unava + $vzdalenost <= 20)
            $this->unava += $vzdalenost;
        else
            echo('Jsem příliš unavený.');
    }

    public function pozdrav(): void
    {
        echo('Ahoj, já jsem ' . $this->jmeno);
    }

    public function __toString(): string
    {
        return $this->jmeno;
    }

}
class Javista extends Clovek
{
    public function __construct(string $jmeno, string $prijmeni, int $vek, public string $ide)
    {
        parent::__construct($jmeno, $prijmeni, $vek);
    }

    public function programuj(): void
    {
        echo("Programuji v {$this->ide}...");
    }

    public function pozdrav(): void
    {
        echo('Hello world! Jsem ' . $this->jmeno);
    }

}

Výstup programu bude následující:

Polymorfismus
localhost

Přepsání metody najdeme v anglické literatuře pod názvem override.

Výhody polymorfismu

Polymorfismus je velmi čistá programátorská technika. Všimněte si, že jsme se vyhnuli jakémukoli větvení. Nemusíme nikde ifovat jestli je instance člověk nebo Javista. Představte si, že bychom podobných potomků lidí měli 100, větvení by potom bylo velmi nepřehledné. Takhle je konkrétní logika pro určitý typ člověka vložena v jeho třídě, kam logicky patří. Hromadná práce s instancemi různých typů nebyla nikdy snadnější. Využití samozřejmě nalezneme i při práci jen s jednou instancí, kde se opět vyhneme větvení.

Potomek se může rozhodnout, zda si metodu přepíše po svém nebo si nechá tu zděděnou. Pokud budete psát programy objektově, často se podobným způsobem vyhnete jinak složitým konstrukcím, jako je dlouhé větvení, vnořené cykly a další.

Klíčové slovo final

V PHP můžeme použít klíčové slovo final, které umožňuje zakázat dědění tříd nebo přepisování metod. Ačkoli je praktický význam této techniky diskutabilní, jako vždy ji zde uvádím pro případ, že byste se s ní někdy setkali.

Finální metody

Přepsání nějaké metody můžeme v nadtřídě zakázat označením metody jako finální. Potomek potom nebude schopen metodu přepsat. Můžeme si to zkusit ve třídě Clovek na metodě pozdrav():

public final function pozdrav(): void
{
    echo('Ahoj, já jsem ' . $this->jmeno);
}

PHP nyní vyhodí při spuštění skriptu Fatal error, jelikož se potomek Javista snaží přepsat onu finální metodu:

Cannot override final method Clovek::pozdrav() in...

Slovo final z definice metody zas odebereme.

Finální třídy

Jako finální můžeme označit i celou třídu. Zakážeme tím kompletně dědičnost a tato třída poté nemůže mít potomky. Opět si to zkusme na třídě Clovek:

final class Clovek
{

    // ...

}

Opět dostáváme Fatal error jelikož javista se snaží z člověka dědit:

Inherit error
localhost

Význam finálních prvků

Jak již bylo zmíněno, význam finálních prvků je poněkud pochybný. Původní myšlenkou bylo zakázat dědění nebo přepisování určitých prvků aplikace. To může být užitečné u velkých frameworků se spoustou tříd, kde tak násilně zabráníme dalšímu zesložiťování aplikace. Prakticky však může mít neuvážené použití slova final nepříjemné následky, kdy nebude možné využít výhod dědičnosti a nevyhneme se psaní redundantního kódu. Doporučoval bych slovo final vůbec nepoužívat.

Automatické načítání tříd

Na konec si ukažme vychytávku, díky které nebudeme muset manuálně requirovat třídy. Je to velmi užitečné hlavně ve chvíli, když jich máme hodně a také proto, že si nemusíme hlídat, zda vše, co používáme, také načítáme. To bývá někdy zdrojem nepříjemných chyb.

PHP ve starších verzích používalo k automatickému načítání tříd magickou metodu __autoload(). Jelikož již existuje její modernější alternativa (funguje od PHP 5.3), ukážeme si rovnou tu.

Ve chvíli, kdy vytvoříme instanci nějaké třídy (nebo ke třídě přistupujeme staticky, viz. dále v seriálu) PHP zkontroluje, zda máme takovou třídu načtenou. V PHP je možné zaregistrovat funkci, která se spustí ve chvíli, kdy PHP zjistí, že nějakou třídu nezná. V této funkci nám od PHP přijde název nenačtené třídy a je na nás, abychom mu ji načetli pomocí funkce require().

Přejděme na začátek souboru index.php a definujme si zde funkci s libovolným názvem, která bere jeden parametr. Pomocí parametru sestavme cestu ke třídě a zavolejme funkci require():

function nactiTridu(string $trida): void
{
    require("tridy/$trida.php");
}

Nyní řekneme PHP, že má volat tuto funkci v případě, když narazí na použití nenačtené třídy:

spl_autoload_register("nactiTridu");

To je celé :) Naše funkce require_once() v souborech Clovek.php a Javista.php můžeme s klidem smazat. Aplikace bude nyní fungovat stejně jako předtím a jakmile použijeme nějakou novou třídu, bude automaticky načtena. Autoloader je potřeba umístit v celé aplikaci pouze jednou, někam na začátek (klidně do indexu). O načítání tříd se již více nemusíme starat. Dnešní kód je ke stažení níže.

V následujícím kvízu, Kvíz - Datové typy, dědičnost a polymorfismus v PHP, si vyzkoušíme nabyté zkušenosti z předchozích lekcí.


 

Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.

Stáhnout

Stažením následujícího souboru souhlasíš s licenčními podmínkami

Staženo 1079x (2.96 kB)
Aplikace je včetně zdrojových kódů v jazyce PHP

 

Předchozí článek
Dědičnost v PHP
Všechny články v sekci
Objektově orientované programování (OOP) v PHP
Přeskočit článek
(nedoporučujeme)
Kvíz - Datové typy, dědičnost a polymorfismus v PHP
Článek pro vás napsal David Hartinger
Avatar
Uživatelské hodnocení:
181 hlasů
David je zakladatelem ITnetwork a programování se profesionálně věnuje 15 let. Má rád Nirvanu, nemovitosti a svobodu podnikání.
Unicorn university David se informační technologie naučil na Unicorn University - prestižní soukromé vysoké škole IT a ekonomie.
Aktivity