Lekce 4 - Zapouzdření v PHP
V předešlém cvičení, Řešené úlohy k 1.-3. lekci OOP v PHP, jsme si procvičili nabyté zkušenosti z předchozích lekcí.
Dnes si v tutoriálu povíme něco o zapouzdření v PHP.
Zapouzdření
OOP stojí na třech základních pilířích: Zapouzdření, Dědičnosti a Polymorfismu. První z nich si dnes probereme.
Zapouzdření umožňuje skrýt některé metody a atributy tak, aby zůstaly použitelné jen pro třídu zevnitř. Objekt si můžeme představit jako černou skřínku (anglicky blackbox), která má určité rozhraní (interface), přes které jí předáváme instrukce/data a ona je zpracovává.
Nevíme, jak to uvnitř funguje, ale víme, jak se navenek chová a používá. Nemůžeme tedy způsobit nějakou chybu, protože využíváme a vidíme jen to, co tvůrce třídy zpřístupnil.
U naší třídy Clovek
by mohl být problém ve chvíli, když
by měla mít atribut $datumNarozeni
a na jeho základě další
atributy: $plnolety
a $vek
. Kdyby někdo objektu
zvenčí změnil $datumNarozeni
, přestaly by platit proměnné
$plnolety
a $vek
. Říkáme, že vnitřní stav
objektu by byl nekonzistentní. Toto se nám ve strukturovaném programování
může klidně stát. V OOP však objekt zapouzdříme a atribut
$datumNarozeni
označíme jako privátní, zvenčí tedy nebude
viditelný. Naopak ven vystavíme metodu zmenDatumNarozeni()
,
která dosadí nové datum narození do proměnné $datumNarozeni
a
zároveň provede potřebný přepočet věku a přehodnocení plnoletosti.
Použití objektu je bezpečné a aplikace stabilní.
Zapouzdření tedy donutí programátory používat objekt jen tím
správným způsobem. Rozhraní (interface) třídy rozdělí na
veřejně přístupné (public
) a vnitřní
strukturu (private
).
Obecně bývá snaha skrýt toho v objektu co nejvíce a zvenčí zpřístupnit jen to důležité. Do data narození se u našich lidí pouštět nebudeme, jelikož výpočet věku není úplně triviální.
Spánek
Privátní atributy slouží tedy objektům ke zpracovávání nějaké jejich vnitřní logiky, která zvenčí není vidět a nemůžeme do ní zasahovat. Do funkce lidí jistě spadá spánek a poslouží tedy jako dokonalý ukázkový příklad.
Člověk bude mít nějakou únavu, která bude uložená v privátním
atributu $unava
.
Dále bude mít metodu spi()
, která bude brát v parametru
počet hodin, které má člověk naspat. Metoda spi()
sníží
únavu podle toho, kolik hodin člověk naspí. Samozřejmě nezapomeneme
ošetřit, aby nebyla únava záporná když se někdo pokusí naspat více
hodin, než je možné.
Aby bylo možné člověka i unavit, dáme mu metodu behej()
,
která bude v parametru brát počet kilometrů. Pro jednoduchost si metodu
naprogramujme tak, aby za každý kilometr přičetla 1 k aktuální hodnotě
únavy. Pokud je únava příliš vysoká, metoda vypíše, že již běhat
nelze.
Třída Clovek
s novou funkcionalitou bude vypadat asi
takto:
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; } }
Všimněte si, že jsme atributu únava při jeho deklaraci rovnou nastavili hodnotu 0. Tato hodnota se nastaví každé instanci při jejím vytvoření a je to tedy alternativa k tomu, kdybychom přiřazení nuly napsali do konstruktoru.
Jinak na třídě není kromě privátního modifikátoru přístupu u
atributu $unava
nic, co bychom již neviděli. Metoda
behej()
není příliš elegantní, jelikož v případě velké
únavy (vyšší než 20 jednotek) přímo vypisuje hlášku, že je člověk
příliš unavený. To snižuje univerzálnost objektu, jelikož takový výpis
nemusíme vždy chtít. Pro naše účely je to však nyní ideální a během
seriálu si ukážeme jak se chybové stavy objektů správně řeší.
Zvenku objektu není možné měnit únavu jinak, než unavením člověka
pomocí metody behej()
nebo necháním člověka vyspat. Tedy tak,
jak jsme to my, autoři třídy, navrhli. Objekt nelze rozbít nebo použít
špatně.
Přesuňme se do index.php
a novou funkcionalitu si otestujme.
Můžeme vymazat volání zdravení na obou našich instancích a jednu z nich
nechme uběhnout 3x 10 km:
{PHP} require_once('tridy/Clovek.php'); $karel = new Clovek('Karel', 'Novák', 30); $jan = new Clovek('Jan', 'Nový', 24); $karel->behej(10); $karel->behej(10); $karel->behej(10); {/PHP}
{PHP} // Obsah souboru Clovek.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}
Výstup programu je:
Karel uběhl 2x 10 km a poté dosáhl stanovené hranice únavy, kterou jsme zvolili na 20 jednotek. Další pokus o běh se tedy již nezdařil a program jednou vypsal, že je příliš unavený.
Dodejme do programu spánek, na 10 km vzdálenosti jsme v kódu zvolili 1 hodinu spánku. Dodejme tedy tuto hodinu před posledních 10 km:
{PHP} require_once('tridy/Clovek.php'); $karel = new Clovek('Karel', 'Novák', 30); $jan = new Clovek('Jan', 'Nový', 24); $karel->behej(10); $karel->behej(10); $karel->spi(1); $karel->behej(10); {/PHP}
{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; } }
Chybová hláška nyní zmizela a vše proběhlo v pořádku.
Když se pokusíme zvenčí (z kteréhokoli jiného souboru, než je
Clovek.php
) přistoupit k privátnímu atributu
$unava
, dostaneme vyhubováno:
$karel->unava = 0;
Příslušné chybové hlášení:
Privátní metody
Privátní mohou být samozřejmě nejen atributy, ale i metody, které vykonávají nějakou funkcionalitu pro vnitřní potřebu třídy. Často se používají také v případě, když máme příliš složitou veřejnou metodu. V tu chvíli ji rozdělíme na několik privátních metod, které veřejná metoda volá. Počet řádků v metodě by ideálně neměl přesáhnout asi 30, počet řádků na třídu asi 500. Nyní je to jedno, u větších projektů a tříd je dobré se tím řídit a v případě velkého rozsahu jednoduše funkcionalitu rozdělit do více komponent, které spolu budou spolupracovat. Jen tak dokážeme udržet naši aplikaci čitelnou a dále rozšiřitelnou.
Aplikaci máte jako vždy níže ke stažení.
V následujícím kvízu, Kvíz - Úvod, třídy a zapouzdření v PHP OOP, si vyzkoušíme nabyté zkušenosti z předchozích lekcí.
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 802x (1.97 kB)
Aplikace je včetně zdrojových kódů v jazyce PHP