NOVINKA: Kurz kybernetické bezpečnosti s akreditací MŠMT, nyní již od 0 Kč. Staň se žádaným profesionálem. Zjisti více:
NOVINKA: Staň se datovým analytikem a získej jistotu práce, lepší plat a nové kariérní možnosti. Více informací:

Lekce 7 - Dědičnost v PHP

V předešlém cvičení, Řešené úlohy k 4.-6. lekci OOP v PHP, jsme si procvičili nabyté zkušenosti z předchozích lekcí.

Dnes si v PHP tutoriálu rozšíříme znalosti o objektově orientovaném programování. V úvodním dílu do OOP jsme si říkali, že OOP stojí na třech základních pilířích: zapouzdření, dědičnosti a polymorfismu. Zapouzdření a používání modifikátoru private nám je již dobře známé. Dnes se podíváme na dědičnost.

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. Vyzkoušíme si to opět na našem příkladu s lidmi.

Pokud si dobře vzpomínáte, všichni naši lidé umí pozdravit, spát a běhat. My budeme ale chtít do naší aplikace přidat javistu (pardon, ale PHPista zní divně :) – když mi napíšete jak se programátorům v tomto jazyce říká, změním to), který bude umět to samé, jako člověk, ale navíc bude umět i programovat.

Přidejte si do složky tridy/ novou třídu Javista.php. Mohli bychom samozřejmě zkopírovat třídu Clovek a jen do ní přidat metodu programuj(). Kód by vypadal asi takto:

<?php

class Javista
{
    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 programuj(): void
    {
        echo('Programuji...');
    }

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

}

Kód je velmi redundantní a když budeme chtít něco změnit v člověku, musíme to změnit i zde a v dalších podobných třídách. Asi uznáte, že toto není vhodný způsob jak aplikaci rozšiřovat.

Upravme nyní třídu tak, aby používala dědičnost:

<?php

class Javista extends Clovek
{

    public function programuj(): void
    {
        echo('Programuji...');
    }

}

PHP nyní třídu Javista oddědí od třídy Clovek. Toho jsme docílili pomocí klíčového slova extends. To znamená, že programátor bude obsahovat všechny metody a všechny atributy, které má třída Clovek plus navíc to, co přidáme do jeho třídy.

Navíc vidíme ve třídě metodu programuj(). Modifikujme kód v souboru index.php tak, aby byl v proměnné $jan javista a nechme ho pozdravit a programovat:

require_once('tridy/Clovek.php');
require_once('tridy/Javista.php');

$karel = new Clovek('Karel', 'Novák', 30);
$jan = new Javista('Jan', 'Nový', 24);

$karel->behej(10);
$karel->behej(10);
$karel->spi(1);
$karel->behej(10);
$jan->pozdrav();
echo('<br />');
$jan->programuj();
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 programuj(): void
    {
        echo('Programuji...');
    }

}

Z výstupu programu vidíme, že vše funguje jak má. Jan má jak atributy a metody člověka, tak javisty:

Dědičnost
localhost

Dědičnost naleznete v anglické literatuře pod pojmem inheritance.

Výhody dědičnosti

Výhody dědění jsou jasné, nemusíme opisovat oběma třídám ty samé atributy, 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 20 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ě.

O mateřské třídě se někdy hovoří jako o předkovi (zde Clovek) a o třídě, která z ní dědí, jako o potomkovi (zde Javista). Potomek může přidávat nové metody nebo si uzpůsobovat metody z mateřské třídy (viz dále). Můžete se setkat i s pojmy nadtřída a podtřída.

V objektovém modelování se dědičnost znázorňuje graficky jako prázdná šipka směřující k předkovi.

Dědičnost objektů – grafická notace - Objektově orientované programování (OOP) v PHP

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, časem si řekneme proč a ukážeme si i jak ji obejít. PHP 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.

Dědičnost a konstruktory

Přidejme našemu javistovi navíc nějaký atribut. Bude se jednat např. o IDE (editor zdrojových kódů), které k programování používá.

Přidejme třídě Javista veřejný atribut $ide a metodu programuj() upravme tak, aby vypisovala i v čem javista programuje:

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

Volání konstruktoru předka

Samozřejmě se nám bude hodit nastavit atribut $ide v konstruktoru javisty. PHP nám konstruktor zdědilo z předka. Pokud potřebujeme jiný, musíme si ho znovu definovat.

Definujme si tedy svůj vlastní. Měl by obsahovat vše co je potřeba k vytvoření předka + naše nové atributy. Abychom nemuseli nastavovat všechny atributy jako jsme to dělali v předkovi, jednoduše nastavíme jen atributy javisty a na zbytek zavoláme konstruktor předka. To uděláme pomocí proměnné parent. Operátoru čtyřtečky :: se nelekejte, vysvětlíme si ho podrobněji dále v seriálu:

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

Nakonec upravíme index.php:

$jan = new Javista('Jan', 'Nový', 24, 'Eclipse');
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}...");
    }

}

A vyzkoušíme:

Dědičnost
localhost

Modifikátor protected

V příkladu výše nebudou v potomkovi přístupné privátní atributy, ale pouze atributy a metody s modifikátorem public. private atributy a metody jsou chápány jako speciální logika konkrétní třídy, která je potomkovi utajena, i když ji vlastně používá, nemůže ji měnit. Můžete si vyzkoušet, že ačkoli javista umí spát, nejsme schopni uvnitř jeho třídy nijak zasahovat do atributu $unava. V tomto případě je to tak správně, protože všichni lidé budou spát stejným způsobem a není důvod, aby do tohoto způsobu potomci lidí zasahovali.

Pokud bychom chtěli nějaký atribut (nebo metodu) nepřístupný zvenčí, ale zároveň přístupný v potomcích, použijeme modifikátor přístupu protected. Vytvořme si pomocnou metodu ve třídě Clovek, která nám vrátí jeho celé jméno (tedy $jmeno + $prijmeni). Metodu budeme chtít používat jen pro vnitřní účely třídy a všech tříd, které z ní dědí. Označíme ji tedy jako protected:

protected function celeJmeno(): string
{
    return $this->jmeno . ' ' . $this->prijmeni;
}

Metodu nyní nebudeme schopni zavolat v souboru index.php (mimo třídu), ale budeme ji schopni zavolat jak ve třídě Clovek, tak ve třídě Javista.

Shrnutí lekce

Dědičnost umožňuje vytvořit novou třídu podle už existující třídy. Potomek zdědí atributy a metody předka a může k nim přidat vlastní funkcionalitu. Díky tomu není nutné opisovat stejný kód do více tříd a úpravy společného chování stačí provést na jednom místě. V PHP se dědičnost zapisuje klíčovým slovem extends a konstruktor předka lze zavolat zápisem parent::__construct(). Modifikátor protected se používá pro členy, které mají být dostupné v dané třídě i v jejích potomcích, ale nemají být přístupné zvenčí programu.

V příští lekci, Polymorfismus, finální prvky a autoloader v PHP, si uvedeme polymorfismus, přepisování metod a autoloader.


 

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 856x (2.79 kB)
Aplikace je včetně zdrojových kódů v jazyce PHP

 

Předchozí článek
Řešené úlohy k 4.-6. lekci OOP v PHP
Všechny články v sekci
Objektově orientované programování (OOP) v PHP
Přeskočit článek
(nedoporučujeme)
Polymorfismus, finální prvky a autoloader v PHP
Článek pro vás napsal David Hartinger
Avatar
Uživatelské hodnocení:
217 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