Lekce 11 - Statika v PHP do třetice
V minulé lekci, Třídní prvky v PHP podruhé - konstanty, jsme popisovali třídní prvky a uvedli jsme si konstanty.
V dnešním díle si vysvětlíme statický registr, databázový wrapper a nakonec budeme polemizovat o tom, kdy použít statiku.
Statický registr
Statický registr je třída, ve které jsou jen statické proměnné, obsahující nějaké hodnoty. Typicky se taková třída používá pro uchování nastavení aplikace. Je potom sdílená v celé aplikaci a obsahuje např. heslo k DB, URL webu, výchozí jazyk a podobně. V neobjektových aplikacích se místo statického registru používaly globální proměnné. Třída by mohla vypadat například takto:
class Nastaveni { public static $udajeDB = array( 'jmeno' => 'root', 'heslo' => '' ); public static $url = 'localhost'; public static $jazyk = 'cz'; }
V celé aplikaci se potom můžeme například ptát na nastavený jazyk nebo toto nastavení změnit:
if (Nastaveni::$jazyk != 'cz') $pozadi = "pozadi_anglicke.png";
Použití statického registru je opět velmi kontroverzní. K uložení konfigurace aplikace není špatný, bývá však velmi často používán k uložení různých instancí objektů, se kterými se v aplikaci dále pracuje. Všechny objekty jsou potom přístupné odevšuď přes registr a to je prostě špatně. Je otázka, jestli se dá o takové aplikaci potom vůbec mluvit jako o objektové.
Databázový wrapper
Poměrně časté a i smysluplné použití statiky je pro databázový
wrapper. Asi jste doteď pracovali se zastaralými neobjektovými databázovými
ovladači (např. funkce mysql_query()
a podobně). V PHP se s
databází pracuje objektově pomocí ovladače PDO (jako PHP Database Objects).
Tento ovladač funguje tak, že se vytvoří instance třídy PDO
a
ta se připojí k databázi. Abychom se k databázi nemuseli připojovat stále
znovu a znovu, sdílíme tuto jednu připojenou instanci v celé aplikaci.
Právě k tomu použijeme statiku. Protože jsme dlouho nedělali nic
praktického a práce s databází je velmi důležitá, zkusíme si to hned
příští lekci, kde si tvorbu wrapperu i lépe popíšeme.
Kontroverze
Jak jsem se již zmínil na začátku tohoto trojčlánku o statice, statika je jedno z nejkontroverznějších témat objektově orientovaného programování. Když se nad tím zamyslíte, tak v podstatě narušuje jeho koncept, jelikož statické prvky jsou přístupné odkudkoli a bez instance. Když máte v aplikaci pár statických tříd (třeba 4) a zbytek normálních, je vše v pořádku. Jakmile ale máte každou druhou třídu statickou, je někde problém.
Vždy platí, že každou aplikaci lze napsat čistě objektově bez použití statiky. Někteří programátoři dokonce zastávají názor, že statiku by jazyky vůbec neměly obsahovat. Samozřejmě to není tak horké, nicméně statika je velmi často špatně užívána a proto zbytek článku věnuji rozebrání správných a špatných využití této techniky.
Správné použití statiky
Když se zamyslíte, udělali jsme si tu několik příkladů a všechny měly společné určité rysy. Jednalo se o pomocné metody nebo pomocné třídy, které se často používají na více místech v aplikaci. Ze třídy často nemá smysl vytvářet instanci a kdyby to šlo, chtěli bychom vždy jen jednu (jednu Matematiku, jednu konzoli, jednu databázi...). V objektových redakčních systémech se staticky často píší také tzv. helpery, což jsou krátké pomocné metody pro formátování výstupu. Na ITnetwork.cz máme např. takový helper, co vypíše vybrané datum slovy:
echo('Dnes je: ' . DatumHelper::slovy($datum));
Dalším příkladem byla ona matematická knihovna, ta dokonce nemá žádný vnitřní stav a jedná se pouze o skupinu pomocných metod.
Do statické proměnné nějaké třídy můžeme také uložit instanci aktuálně přihlášeného uživatele, aktuálně zobrazeného článku a podobně. To jsou opět věci, které je potřeba sdílet v mnoha třídách.
Statika v uznávaných projektech
Statiku nalezneme v základních objektových knihovnách uznávaných programovacích jazyků, jako jsou např. Java nebo C# .NET. Statika je zde použita přesně pro pomocné metody, jako zobrazení jednoduché zprávy nebo třeba vypsání textu do konzole:
Console::writeLine("Ema má mámu."); MessageBox::show("Špatné heslo"); $vysledek = Databaze::dotaz("SELECT * FROM `clovek`");
Konzoli máme jen jednu a pracujeme s ní ve velkém počtu tříd, proto dává smysl mít její třídu jako statickou. Podobně je to s databází, aplikace téměř vždy používá právě jednu databázi a to z mnoha míst.
Špatné použití statiky
Když se budete rozmýšlet, zda máte statiku použít, tak ji raději
nepoužívejte Na 90 % je
totiž zbytečná. Statiku byste neměli používat v jiných případech než
ve výše zmíněných. Pokud nevytváříte nějakou systémovou třídu,
která se bude používat všude, určitě by neměla být statická.
I například třída, která slouží ke správě lidí a obsahuje metody
jako registruj()
, vymaz()
a podobně by neměla být
statická a je lepší, když si vždy, kdy potřebujete s lidmi pracovat,
vytvoříte její instanci. Třída by se mohla jmenovat třeba
SpravceLidi
. Nemělo by vás napadnout udělat ani statickou
galerii, statické lidi a podobně. Celé tyto 3 články jsem psal
hlavně proto, abyste statický kód uměli číst. Neznamená to, že ho máte
psát a určitě budete často narážet v cizím kódu na statické
prvky, které by statické být neměly.
Předávání závislostí a dependency injection
Statika se používá většinou k předávání závislostí, tedy tříd, které potřebují ostatní třídy k tomu, aby fungovaly. Jedná se např. o již zmíněnou databázovou třídu nebo o nastavení aplikace. O řešení závislostí mezi objekty v aplikacích jsou napsané celé knihy a existuje milion přístupů. Je to jedno z nejřešenějších témat v objektově orientovaném programování. Těžko říci, jestli je pro začátečníky vhodnější statika nebo řešit závislosti pomocí návrhových vzorů, mně připadá statika jednoduchá a při rozumném použití i přehledná.
Většinou platí, že v systému do velikosti pár desítek tříd vám stačí občas použít statiku a aplikaci to nějak vážně neuškodí. Dělají to tak uznávané frameworky, dělám to tak i já. S většími projekty se zvyšuje počet závislostí (zejména když používáte knihovny třetí strany, což se snažím dělat minimálně) a tím i počet statických tříd. Jakmile v aplikaci staticky sdílíte více než pár prvků, je lepší od statiky upustit a předávat každé třídě např. v konstruktoru to, co potřebuje. Jen tak se aplikaci podaří uhlídat a nestane se spletí vzájemně provázaných statických tříd, kde člověk už neví co je na čem závislé. Právě z toho důvodu někteří programátoři doporučují statiku vůbec nepoužívat.
Technice předávání závislostí automaticky a bez statiky se říká
Dependency Injection (zkráceně DI), více si o ní můžete přečíst např.
v článku Dependency
injection. Dalším řešením, které jsem si oblíbil zejména při
tvorbě her, je vytvořit si sdílené instance v nějakém kontejneru a
objektům poté v konstruktoru předávat tento kontejner (vzoru se říká
service locator). Vyhneme se statice a např. ve hře se jednoduše ze třídy
Hrdina
dostaneme na instanci třídy Kontejner
a přes
něj na mapu, ostatní objekty, nastavení hry, databázovou třídu a
podobně.
Jak řešit závislosti v reálném systému si ukážeme jindy. Téma statiky jsme tedy probrali.
V následujícím cvičení, Řešené úlohy k 9.-11. lekci OOP v PHP, si procvičíme nabyté zkušenosti z předchozích lekcí.