Pouze tento týden sleva až 80 % na e-learning týkající se C# .NET. Zároveň využij akci až 30 % zdarma při nákupu e-learningu - Více informací.
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í.
C# .NET + discount 30

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:

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
{

    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;
    }

}
class Javista extends Clovek
{

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

}
Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!

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');
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;
    }

}
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:

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.

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

 

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 Čápka
Avatar
Uživatelské hodnocení:
68 hlasů
David je zakladatelem ITnetwork a programování se profesionálně věnuje 13 let. Má rád Nirvanu, sushi 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

 

 

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

Avatar
Marian Bubenik:9.2.2019 9:29

OK, díky, pokud zjistím, co dělám blbě, dám to sem a ozvu se...

 
Odpovědět
9.2.2019 9:29
Avatar
Láďa
Člen
Avatar
Láďa:20.5.2019 9:14

Ahoj, zkoušel jsem udělat potomka třídy Imagick:

<code>

class ftImagick extends Imagick {

public $file;
public $rozmer;

public function __construct($file, $rozmer,$rozmer_tmb){

//proč funguje jen tohle??
$this->file=$file;
parent::__con­struct($file);

$this->rozmer=$rozmer;
$this->rozmer_tmb=$roz­mer_tmb;
// $this->file=$file;

}

public function zmensi(){

$info = getimagesize($this->file);
$sirka = $info[0]; $vyska = $info[1];

/*
if ($sirka > $vyska) {
if ($sirka > $this->rozmer) $scle = 1;
}else if ($sirka <= $vyska) {
if ($vyska > $this->rozmer) $scle = 1;}
*/

if (($sirka > $this->rozmer)||($vyska > $this->rozmer)) $scle = 1;

if ($scle == 1) {
// pokud chces zmensit obrazek na maximalni velikost:
$this->scaleImage($this->rozmer, $this->rozmer, true);
$this->writeImage($this->file);
unset ($scle);
}
}
}

</code>

 
Odpovědět
20.5.2019 9:14
Avatar
Rudolf Pecinovský:23.12.2020 11:38

Z výkladu vyplývá, že konstrukce dědění slouží především k tomu, abychom snadno získali dříve naprogramovaný kód. Nějak jsi ale zapomněl zdůraznit LSP, který upozorňuje, že množina instancí dceřiné třídy musí být podmnožinou instancí rodičovské třídy, jinými slovy že instance potomka se musí chovat jako plnohodnotná instance předka. Když se toto nezdůrazní, pak programátoři vytvářejí např. kruhy jako potomky bodů (předají atribut poloměr) a kruhové výseče jako potomky kruhů (přidají úhel), takže krajními body úsečky mohou být dvě kruhové výseče. Když se takováto základní pravidla nedodržují, začne se po čase program hroutit vlastní vahou.

 
Odpovědět
23.12.2020 11:38
Avatar
Pavel Kubalík:3.2.2021 15:11

Dobrý den
Vyřešil jsem první příklad (Nákladní automobil) a moc za něj děkuji. Opravdu jsem se na něm "vyřádil". Ne že by mi dal moc práce, ale vymýšlel jsem různé varianty. Docela mě to šlo, takže si myslím, že tuto lekci mohu uzavřít. Trochu mě zdržel obrázek náklaďáku. Nejdříve jsem špatně vložil složku obrazky (do tridy) a potom jsem zjistil, že obrázek má být uprostřed. Nechtělo se mi dělat soubor css, tak jsem to uzavřel do div a použil style=. Šlo to. Trochu mě zdržel název třídy. já jsem zvolil NákladniAuto a mělo být NakladniAutomobil. Ale zvládnul jsem to.
Díky PK

 
Odpovědět
3.2.2021 15:11
Avatar
Yuriy Tretyachenko:23.3.2021 21:30

A mohl bys ukazat kod? me jde o te ruzne varianty

Editováno 23.3.2021 21:32
 
Odpovědět
23.3.2021 21:30
Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!
Avatar
Yuriy Tretyachenko:12.4.2021 16:28

Ted mi na email prisla nabidka a hlavicka znela takto: "Krademe PHPkáře korporátům". Takze PHPista jeste i PHPkář. Ale mohlo by to take byt treba PHPač, PHPník, PHPec, PHPator atd.

 
Odpovědět
12.4.2021 16:28
Avatar
Vincent
Člen
Avatar
Vincent:18.8.2021 19:56

at dělám co dělám tak mi vyhazuje v Javista.php na obrazovku Fatal error: Uncaught Error: Class "Clovek" not found in C:\xampp\htdoc­s\OOP\tridy\Ja­vista.php:3 Stack trace: #0 {main} ....nevydržel jsem to a zkopiroval vaše všechny tři soubory,přepsal ty své a stejně ne...nenapadá mě co dělám špatně když mám ted vlastně totožné kody jako zde.děkuji za jakoukoliv odpověd. Vé

 
Odpovědět
18.8.2021 19:56
Avatar
Tomáš Teplík:16. ledna 15:27

Asi označení jako PHPčkář ;-)

 
Odpovědět
16. ledna 15:27
Avatar
Odpovídá na Vincent
Tomáš Teplík:16. ledna 15:30

Ahoj, podobné chyby jsem dosáhl, pokud jsem si přehodil linkování souborů
require_once('tri­dy/Javista.php');
require_once('tri­dy/Clovek.php');
Fatal error: Uncaught Error: Class "Clovek" not found in .../vyvoj/tri­dy/Javista.php:3 Stack trace: #0 .../vyvoj/index­.php(12): require_once() #1 {main} thrown in .../vyvoj/tri­dy/Javista.php on line 3

 
Odpovědět
16. ledna 15:30
Avatar
Odpovídá na Tomáš Teplík
Luděk Štrobl:14. dubna 8:46

možná by se uchytilo i PéHáPer...

 
Odpovědět
14. dubna 8:46
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 43. Zobrazit vše