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 č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:
{PHP} 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();
{PHP} class Clovek { 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 __toString() { return $this->jmeno; } }
{PHP} class Javista extends Clovek { public function programuj() { 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 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.

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:
{PHP} require_once('tridy/Clovek.php'); require_once('tridy/Javista.php'); $karel = new Clovek('Karel', 'Novák', 30); $jan = new Javista('Jan', 'Nový', 24, 'Eclipse'); $karel->behej(10); $karel->behej(10); $karel->spi(1); $karel->behej(10); $jan->pozdrav(); echo('<br />'); $jan->programuj();
{PHP} class Clovek { 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 __toString() { return $this->jmeno; } }
{PHP} class Javista extends Clovek { public $ide; public function __construct($jmeno, $prijmeni, $vek, $ide) { $this->ide = $ide; parent::__construct($jmeno, $prijmeni, $vek); } public function programuj() { echo("Programuji v {$this->ide}..."); } }
A vyzkoušíme:
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.
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 749x (1.31 kB)
Aplikace je včetně zdrojových kódů v jazyce PHP
Komentáře


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