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

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

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

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:

Your page
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 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:

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

 

 

Článek pro vás napsal David Čápka
Avatar
Jak se ti líbí článek?
20 hlasů
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.
Aktivity (5)

 

 

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

Avatar
Danny
Člen
Avatar
Danny:20.7.2015 0:54

A nebo :)

 
Odpovědět 20.7.2015 0:54
Avatar
loading84
Člen
Avatar
loading84:11.10.2015 20:30

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):11.10.2015 21:00

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á na Martin Konečný (pavelco1998)
loading84:11.10.2015 21:54

Dík, tohle mi teda funguje.

 
Odpovědět 11.10.2015 21:54
Avatar
Tomáš Vlček:17.2.2016 12:37

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.2.2016 12:38
 
Odpovědět  +2 17.2.2016 12:37
Avatar
Mego
Člen
Avatar
Odpovídá na Tomáš Vlček
Mego:17.2.2016 14:08

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

Odpovědět  ±0 17.2.2016 14:08
Radšej 15 minút skôr, ako 15 sekúnd neskoro...
Avatar
bonroykid
Člen
Avatar
bonroykid:21.3.2016 14:57

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.3.2016 14:57
Avatar
Odpovídá na bonroykid
Pavel Habžanský:21.6.2016 17:53

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.6.2016 17:53
Čím větší výzva, tím větší zkušenost
Avatar
Aleš Hamerle:4.10.2016 9:54

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.10.2016 9:54
Avatar
urso
Člen
Avatar
Odpovídá na Aleš Hamerle
urso:26. ledna 11:34

Ahoj,
asi je problém v jednoduchých uvozovkách, je rozdíl napsat

echo "Něco";

a

echo 'Něco';
Odpovědět 26. ledna 11:34
Ne estas pano sen laboro!
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 21. Zobrazit vše