IT rekvalifikace s garancí práce. Seniorní programátoři vydělávají až 160 000 Kč/měsíc a rekvalifikace je prvním krokem. Zjisti, jak na to!
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í.

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.

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 810x (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í:
162 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