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:
{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 { 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; } }
{PHP} 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 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.

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