7. díl - Dědičnost v PHP

PHP Objektově orientované programování Dědičnost v PHP American English version English version

V minulém tutoriálu o objektově orientovaném programování v PHP jsme si vysvětlili rozdíl mezi referenčními a primitivními datovými typy. Dnes si opět 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 člověk a jen do ní přidat metodu programuj(). Kód by vypadal asi takto:

<?php

class Javista
{

    public $jmeno;
    public $prijmeni;
    public $vek;
    private $unava = 0;

    public function __construct($jmeno, $prijmeni, $vek)
    {
        $this->jmeno = $jmeno;
        $this->prijmeni = $prijmeni;
        $this->vek = $vek;
    }

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

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

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

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

    public function __toString()
    {
        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()
        {
                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 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();

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 v PHP

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 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ě.

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

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:

public $ide;

A metodu programuj() upravme tak, aby vypisovala i v čem javista programuje:

public function programuj()
{
        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($jmeno, $prijmeni, $vek, $ide)
{
        $this->ide = $ide;
        parent::__construct($jmeno, $prijmeni, $vek);
}

Nakonec upravíme index.php:

$jan = new Javista('Jan', 'Nový', 24, 'Eclipse');

A vyzkoušíme:

Volání konstruktoru předka v PHP

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 zů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()
{
        return $this->jmeno . ' ' . $this->prijmeni;
}

Metodu nyní nebudeme schopni zavolat v index.php (mimo třídu), ale budeme ji schopni zavolat jak ve třídě Clovek, tak ve třídě Javista. Pokračovat budeme zas příště, kdy si uvedeme polymorfismus.


 

Stáhnout

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

 

  Aktivity (2)

Článek pro vás napsal David Čápka
Avatar
Autor pracuje jako softwarový architekt a pedagog na projektu ITnetwork.cz (a jeho zahraničních verzích). Velmi si váží svobody podnikání v naší zemi a věří, že když se člověk neštítí práce, tak dokáže úplně cokoli.
Unicorn College Autor se informační technologie naučil na Unicorn College - prestižní soukromé vysoké škole IT a ekonomie.

Jak se ti líbí článek?
Celkem (16 hlasů) :
55555


 



 

 

Komentáře
Zobrazit starší komentáře (10)

Avatar
Martin Konečný (pavelco1998):

... nebo použiješ autoloader, který se ty require postará sám :)

 
Odpovědět  +1 20.7.2015 0:49
Avatar
loading84
Člen
Avatar
loading84:

Jak ověřím, že tohle funguje?

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

když volám na indexu

$karel->celeJmeno();
$jan->celeJmeno();

tak to nereaguje. A ani kdyz dam misto protected public.

 
Odpovědět 11.10.2015 20:30
Avatar
Odpovídá na loading84
Martin Konečný (pavelco1998):

return jen vrací, nic nevypisuje. Použij

echo $karel->celeJmeno();
 
Odpovědět  +1 11.10.2015 21:00
Avatar
loading84
Člen
Avatar
 
Odpovědět 11.10.2015 21:54
Avatar
Tomáš Vlček:

PHP - péhápé, tedy péhápkář
Reaguji na to, jak se říká člověku, který programuje v PHP.
Žádný jiný název jsem neslyšel. Třeba to tady máme jen na Severní Moravě :)
A namísto Javista bych u nás použil možná Javař, ale jistý si nejsem. Žádného neznám :)
Každopádně super články, narazil jsem na ně náhodou při hledání v Google.

Editováno 17. února 12:38
 
Odpovědět  +1 17. února 12:37
Avatar
Mego
Člen
Avatar
Odpovídá na Tomáš Vlček
Mego:

My tomu hovorím pehapečkár a džavista :D

Odpovědět  -1 17. února 14:08
Radšej 15 minút skôr, ako 15 sekúnd neskoro...
Avatar
bonroykid
Člen
Avatar
bonroykid:

Když změním konstruktor v rodičovi, tak abych hledal všechny třídy v kterých se konstruktor dědí a všude to přepisoval? To se mi zdá hodně nešikovné. Asi z toho bude spousta chyb.

 
Odpovědět  +1 21. března 14:57
Avatar
Odpovídá na bonroykid
Pavel Habžanský:

Nepřepisuješ, od toho v konstruktoru potomka voláš konstruktor rodiče, abys to nemusel všude přepisovat... Horší je, když se ti změní argumenty v konstruktoru

Odpovědět 21. června 17:53
Čím větší výzva, tím větší zkušenost
Avatar
Aleš Hamerle:

Ahoj všem,
v první řadě chci poděkovat každému jednomu z vás za super stránky.
Mám dotaz:
public function programuj()
{
echo("Programuji v {$this->ide}...");
}
Tohle mi zkrátka nefunguje, až po úpravě do následujícího tvaru to funguje dle očekávání.
public function programuj()
{
echo('Programuji v ' .$this->ide.'...');
}
Nevíte proč tomu tak je? Jsem začátečník a rád bych to pochopil z gruntu. Děkuji moc za odpověď.
Logicky si myslím, že by to na funkci kódu nemělo mít negativní vliv, ale rád bych rozuměl tomu co dělám, tak by mě zajímalo v čem je problém. Verze php? Děkuji za odpověď a přeji hezký den.

 
Odpovědět 4. října 9:54
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.

Zobrazeno 10 zpráv z 20. Zobrazit vše