Lekce 9 - Statika v PHP
PHP
Objektově orientované programování
Statika v PHP
English version


V minulé lekci, Polymorfismus, finální prvky a autoloader v PHP, jsme si vysvětlili jak funguje polymorfismus a naučili se používat autoloader pro automatické načítání tříd. V dnešním PHP tutoriálu se seznámíme s velmi kontroverzní technikou - statikou.
POZOR! Dnešní
lekce vám ukáže statiku, tedy postupy, které v podstatě narušují
objektový model. OOP je obsahuje jen pro speciální případy a obecně
platí, že vše jde napsat bez statiky. Vždy musíme
pečlivě zvážit, zda statiku opravdu nutně potřebujeme. Obecně
bych doporučoval statiku vůbec nepoužívat, pokud si nejste
naprosto jisti, co děláte. Podobně, jako globální proměnné je statika v
objektovém programování něco, co umožňuje psát špatný kód a porušovat
dobré praktiky. Znalosti použijte s rozvahou, na světe bude potom méně
zla.
Třídní prvky
V objektově orientovaném programování jsme již trochu sběhlí a proto víme, že aplikace je složená z množiny komunikujících objektů. Objekty jsou vždy instance, vytvořené podle nějaké třídy. Každá instance je unikátní a má svůj vnitřní stav (atributy) a schopnosti (metody).
Objektově orientované programování však kromě instančních prvků umožňuje definovat i prvky třídní. Jedná se o atributy a metody, které nepatří instanci, ale patří třídě. Třídní metodu tedy voláme na třídě, místo na instanci a instanci vůbec nepotřebujeme. Takovou metodu označíme klíčovým slovem static a můžeme ji zavolat odkudkoli z programu aniž bychom měli instanci. Zkusme si to na jednoduchém příkladu.
Matematická třída
Vytvořme si ve složce "tridy" novou třídu Matematika.php. Do ní nejprve umístíme instanční metodu naDruhou(), jak jsme byli zvyklí doposud:
class Matematika { public function naDruhou($zaklad) { return $zaklad *$zaklad; } }
V index.php si zkusme nechat vypočítat druhou mocninu pomocí naší metody. Vytvoříme si instanci naší třídy Matematika a zavoláme na ní příslušnou metodu:
{PHP} require_once('tridy/Matematika.php'); $matematika = new Matematika(); echo($matematika->naDruhou(12));
{PHP} class Matematika { public function naDruhou($zaklad) { return $zaklad *$zaklad; } }
Výstupem programu je samozřejmě číslo 144, ale o ten tu teď nejde. Představte si, že v nějaké výpočetní aplikaci používáte na mnoha místech v mnoha třídách nějaké podobné pomocné výpočty. Bylo by poněkud nepraktické stále tvořit nové instance nebo si předávat instanci třídy Matematika. Z toho důvodu uděláme metodu naDruhou() statickou. Upravme třídu Matematika na tuto podobu:
class Matematika { public static function naDruhou($zaklad) { return $zaklad *$zaklad; } }
V index.php odstraníme vytvoření instance a metodu naDruhou() budeme volat přímo na třídě Matematika. K volání třídních prvků nepoužíváme operátor šipky (->), ale čtyřtečku (::):
{PHP} require_once('tridy/Matematika.php'); echo(Matematika::naDruhou(12));
{PHP} class Matematika { public static function naDruhou($zaklad) { return $zaklad *$zaklad; } }
Metodu jsme zavolali bez instance. Dá se říci, že je také globální a viditelná odkudkoli, což může při špatném použití způsobit velmi špatnou čitelnost kódu (viz dále). Víme, že OOP se snaží spíše co nejvíce věcí skrývat. V tomto případě je to však v pořádku a dále v seriálu si vysvětlíme další správná i špatná užití.
Kombinace statických a instančních prvků
Třída nemusí obsahovat jen instanční nebo jen statické prvky, ale může obsahovat jejich kombinaci. Instanční prvky jsou poté přístupné jen na instanci, statické zase jen na třídě.
Číslování instancí
O statických prvcích můžeme zjednodušeně říci, že jsou společné pro všechny instance, jelikož patří té jedné třídě. Vraťme se zas k našim lidem a dejme tomu, že je budeme potřebovat číslovat. Každý člověk bude mít atribut $id, ve kterém bude unikátní číslo. První vytvořený člověk bude mít $id=1, druhý $id=2 a tak dále. Kde ale uchovávat kolik lidí jsme již vytvořili?
Pokud známe statiku, není nic jednoduššího než přidat do třídy Clovek statický atribut pocetLidi, nastavený na 0. Třída si tedy pamatuje kolik lidí bylo vytvořeno. Je to i logicky správně, jelikož atribut je sdílený mezi všemi instancemi a proto je na třídě. V konstruktoru člověka poté tento statický atribut vždy zvýšíme o 1 a do instanční proměnné $id dosadíme toto číslo.
Třída Clovek.php bude vypadat s touto malou úpravou následovně:
class Clovek { public $jmeno; public $prijmeni; public $vek; private $unava = 0; public $id; private static $pocetLidi = 0; public function __construct($jmeno, $prijmeni, $vek) { $this->jmeno = $jmeno; $this->prijmeni = $prijmeni; $this->vek = $vek; self::$pocetLidi++; $this->id = self::$pocetLidi; } 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); } protected function celeJmeno() { return $this->jmeno . ' ' . $this->prijmeni; } public function __toString() { return $this->jmeno . ' - ' . $this->id; } }
Při bližším pohledu vidíme 2 nové atributy, instanční $id a třídní $pocetLidi.
V konstruktoru inkrementujeme (zvýšíme o 1) proměnnou $pocetLidi a hodnotu dosadíme nové instanci do jejího $id. Ke statickým prvkům se uvnitř třídy přistupuje pomocí klíčového slova self. Má podobný význam jako $this, které uchovávalo aktuální instanci, self uchovává aktuální třídu. Matoucí může být, že se za čtyřtečku na rozdíl od $this píše dolar.
Poslední změna spočívá v metodě __toString(), kde vracíme i ID, abychom jednoduše viděli, že nám jejich přidělování funguje.
V index.php poupravíme cyklus tak, aby místo zdravení lidi vypisoval. Pro úplnost dodávám i jejich tvoření:
{PHP} require_once('tridy/Clovek.php'); require_once('tridy/Javista.php'); $lide = array(); $lide[] = new Clovek('Karel', 'Novák', 30); $lide[] = new Javista('Jan', 'Nový', 24, 'Eclipse'); $lide[] = new Clovek('Josef', 'Nový', 50); $lide[] = new Javista('Tomáš', 'Marný', 28, 'NetBeans'); $lide[] = new Clovek('Marie', 'Nová', 32); foreach ($lide as $clovek) { echo($clovek . '<br />'); }
{PHP} class Clovek { public $jmeno; public $prijmeni; public $vek; private $unava = 0; public $id; private static $pocetLidi = 0; public function __construct($jmeno, $prijmeni, $vek) { $this->jmeno = $jmeno; $this->prijmeni = $prijmeni; $this->vek = $vek; self::$pocetLidi++; $this->id = self::$pocetLidi; } 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); } protected function celeJmeno() { return $this->jmeno . ' ' . $this->prijmeni; } public function __toString() { return $this->jmeno . ' - ' . $this->id; } }
{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}..."); } public function pozdrav() { echo('Hello world! Jsem ' . $this->jmeno); } }
Výstup programu:
Vidíme, že statický atribut $pocetLidi opravdu náleží třídě Clovek.
Když PHP třídu poprvé načte, vytvoří atribut a nastaví do něj hodnotu
0. Statický atribut na třídě Clovek v paměti přetrvává do skončení
skriptu bez ohledu na to, zda existují nějaké instance lidí nebo ne. S jeho
hodnotou můžeme manipulovat z instančních metod (zde z konstruktoru).
Ze statické metody nelze volat metoda instanční, jelikož k
tomu potřebujeme instanci
Naopak to tedy nelze.
Bez statiky by se tento příklad řešil pomocí objektu, který by lidi vyráběl. Takovým objektům se říká továrny, ale o tom zas někdy jindy.
Statika je velmi důležitá, velmi používaná a často velmi mylně chápaná programátorská technika. Z toho důvodu ji budou věnované ještě 2 následující lekce. Řekneme si kdy ji používat a kdy nikoli. Budete na ni narážet často a je velmi důležité, abyste chápali jak funguje. V příští lekci, Třídní prvky v PHP podruhé - konstanty, budeme pokračovat a vysvětlíme si jak se v PHP používají konstanty. Soubory z dnešní lekce jsou jako vždy ke stažení v archivu níže.
Stáhnout
Staženo 446x (1.59 kB)
Aplikace je včetně zdrojových kódů v jazyce PHP
Komentáře


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