K napsání tohoto článku mě přiměla mezera v originální dokumentaci k PHP, kde je práce s objekty popsána jen okrajově a mnoho důležitých faktů v ní není zmíněno vůbec. Také další odborná literatura o PHP, která v České republice vyšla, objektově orientované programování v PHP popisuje jenom povrchně. V tomto seriálu se vám pokusím předat ucelený materiál, abyste celou problematiku bez problémů zvládli a mohli OOP v PHP skutečně uplatnit – nebo přečíst a zavrhnout.
Telegraficky o tom, co nás v tomto a příštích pokračováních čeká:
- Co je to OOP
- Definice tříd, jejich atributů a metod
- Pojednání o konstruktorech
- Dědičnost, volání předefinovaných metod
- Statická volání metod
- Polymorfizmus
- Problémy s operátorem přiřazení
- …a něco navíc
Chvíli jsem uvažoval, jestli by název tohoto článku neměl raději znít „V PHP obětí objektů“, protože pokusy o objektové programování v PHP vám místy přinesou nejednu pernou chvilku. Jako třeba… Začněme ale pěkně od začátku.
Stručně o objektově orientovaném programování (OOP)
OOP je metoda tvorby programů, která víceméně věrně napodobuje způsob, jakým zacházíme s předměty reálného světa. Tento způsob programování je přehlednější, více strukturovaný, modulárnější a abstraktnější, než klasické procedurální programování.
Objekt je jednoznačně identifikovatelná entita, která má určité vlastnosti (atributy) a chování. Vezměme si třeba obyčejný budík. Jeho vlastnostmi jsou například barva, tvar a druh zvonění. Funkční budík měří čas a ve zvolený okamžik začne zvonit – takové je jeho chování. Každý z nás umí s budíkem zacházet a nikoho už nezajímá, jak uvnitř funguje. Víme, jak z budíku odečíst přesný čas a jak jej nastavit, a to nám stačí k tomu, abychom jej mohli úspěšně používat. OOP ztělesňuje nástroj, kterým lze kód chování „ukrýt“ uvnitř objektu. Až se naučíme definovat třídy objektů, poznáme, že objekty jsou programátorskou konstrukcí sdružující dohromady proměnné (vlastnosti, atributy) a funkce, které s těmito proměnnými pracují (akce, chování). Uživateli objektů stačí znát jenom určité funkce objektu (rozhraní) a o vnitřní implementaci se nemusí starat. Tento koncept je známý pod pojmem zapouzdření: vnitřní implementace mechanismu je oddělena od jeho rozhraní.
Podívejme se na budíky trochu s nadhledem. Všechny plní tutéž úlohu, a přitom se každé jejich provedení tak liší: existují budíky mechanické, elektronické nebo s rádiem, digitální nebo s ručičkama a podobně. Přitom je zřejmé, že jsou si všechny velice podobné. Bavíme se tady o budících, ale každý z nás si představí nějaký jiný. Cítíme, že základní vlastnosti všech budíků světa lze shrnout do abstraktního pojmu „budík“. Každý konkrétní druh je z tohoto abstraktního budíku odvozen – v řeči OOP jaksi dědí všechny vlastnosti abstraktního budíku a staví na nich svá vylepšení.
Přesuňme se teď k programování. Všechny nastíněné principy lze v OOP uplatnit. Objektem je datová struktura se sadou atributů (proměnných) a množstvím funkcí, které s daty pracují. Funkce přiřazené k objektům se v OOP nazývají metody. Data i obslužné metody jsou v objektu zapouzdřeny a navenek ukazují jen svá rozhraní.
Definici objektu je možno zdědit a vytvořit tak nový typ objektu, který může ke stávajícím atributům přidávat nějaké nové vlastnosti, stejně tak jako dodefinovávat (nebo předefinovávat!) zděděné metody. V programech vytváříme rozsáhlou hierarchii příbuzných modulů – objektů. Na vrcholu stromu stojí nejabstraktnější objekt se základními atributy a funkcemi společnými pro všechny. Jeho vlastnosti každý objekt dědí, přidává své atributy a chování – specializuje se až ke konkrétním nástrojům. Výhodami jsou opakovaná použitelnost a přehlednost kódu.
Třídy
Třída je šablonou objektu. Lze si ji představovat jako formičku, do které se „nalejou“ data a vznikne instance (výskyt) objektu.
Abychom mohli vytvářet nějaké objekty, musíme si nejprve připravit patřičné „formy“, tj. třídy. Třída se definuje pomocí příkazu class
. V našem příkladu se pokusíme implementovat jednoduché menu. Každá třída musí mít nějaký název, který se píše za klíčové slovo class
. Naši třídu nazveme prozaicky „ Menu
“:
class Menu {
# tady budou definice atributů a metod...
}
Začněme nyní definovat nějaké atributy. Před deklarací vlastnosti se píše klíčové slovo var
. Za ním následuje seznam atributů, oddělených čárkou. Proměnným může být rovnou přiřazena hodnota – v takovém případě bude mít každý nový objekt příslušné atributy automaticky inicializovány.
class Menu {
var $polozky; # pole položek menu
var $podklad = '#FFFF00'; # barva pozadí
}
Zda píšete var
před každý název atributu, nebo vlastnosti městnáte za jeden var
a identifikátory oddělujete čárkou, je úplně jedno. Vyberte si jeden způsob a ten používejte. Jejich kombinace působí rušivě a přispívá k nepřehlednosti.
Poznámka: Třídy můžete používat také jako obyčejný datový typ záznam (struct v C, record v Pascalu), nadefinujete-li jim jenom atributy, ale žádné metody. Kdo by se ale takto okrádal o sílu OOP?
Pro přístup k atributům objektu se používá operátor ->
. Máme-li vytvořen objekt s názvem $moje_menu
(jak se ze tříd vyrábějí objekty, se dozvíme příště), který obsahuje atribut $podklad
, manipulujeme s ním takto:
Uvedený příklad nejprve do atributu podklad
objektu $moje_menu
uloží hodnotu #33FF33
, a později ji vytiskne na stránku.
Všimněte si, že znak dolaru ( $
) se vkládá před název atributu při jeho definici ve třídě (za klíčové slovo var
), ale ne při odkazování pomocí operátoru ->
.
Poznámka: Samozřejme vám nic nebrání v používání konstrukcí typu:
$promenna = 'pozdrav';
$muj_objekt->$promenna = 'ahoj';
Výsledkem bude, že se řetězec „ahoj“ uloží do atributu, jehož název je uchován v proměnné $promenna, tedy do atributu $pozdrav.
Poznámka: Poctivě řečeno, PHP nijak netrvá na deklarování atributů třídy pomocí var. Nové atributy můžete do objektu klidně přidávat „za chodu“. Např. následující kód je úplně v pořádku:
class MojeTrida {
var $nazev;
}
/*
... tady se vytvoří objekt moje_trida
podle třídy MojeTrida (jak na to, viz dále)
*/
$moje_trida->nazev = '1.B';
$moje_trida->pocet_zaku = 15;
...
print 'Třída ' . $moje_trida->nazev
. 'má ' . $moje_trida->pocet_zaku . ' žáků.';
Bez jakýchkoliv rozpaků jsem použil nový atribut pocet_zaku a PHP to ochotně zbaští. Chtěl bych vás ale před takovou nedbalostí varovat. Velice to zvyšuje nepřehlednost kódu.
Pokračování příště…