V této chvíli se nemůžu nezmínit o obecném významu konstrukce ( NazevTridy::nazevMetody()). Význam tohoto příkazu závisí tom, v jakém kontextu je proveden.
Je-li příkaz proveden vně jakékoliv metody, znamená to statické volání metody, tj. provedení metody třídy bez návaznosti na nějaký konkrétní objekt. Například takovýto kód PHP je úplně v pořádku (pro inspiraci jsem si odskočil do API Javy):
<?
class Math {
function sin($arg) {
return sin($arg);
}
#...
}
/*
K statickému volání metody sin() třídy Math
nepotřebujeme vytvářet žádný objekt této třídy:
*/
print Math::sin(0);
?>
Staticky volaná metoda se chová jako obyčejná funkce. Otázkou může být, jak se bude nakládat s proměnnou $this – úplně normálně. Po statickém zavolání metody je $this nedefinovaná lokální proměnná, která se po opuštění metody, stejně jako ostatní lokální proměnné, zruší. Jakákoliv práce s $this (např. $this->atribut = 'hodnota';) má platnost jen uvnitř metody a pochopitelně se nepromítne do žádného existujícího objektu.
Trochu jiná situace nastane, zavoláte-li takto nějakou metodu uvnitř jiné metody konkrétního objektu. Potom proměnná $this obsahuje náš objekt a volaná metoda jej může jakkoliv modifikovat. Ošidné je to, že volaná metoda může být úplně z jiné třídy, která třeba není v žádném příbuzenském vztahu s třídou vašeho objektu. Posuďte následující příklad.
class TridaA {
var $promenna = '';
function inicializuj() {
$this->promenna = 'hodnota';
}
function delejNeco() {
print 'Jsem v metodě třídy TridaA<BR>';
$this->inicializuj();
print $this->promenna;
}
}
class TridaB {
function delejNecoJineho() {
print 'Jsem v metodě třídy TridaB<BR>';
TridaA::delejNeco();
}
}
$objekt_b = new TridaB;
$objekt_b->delejNecoJineho();
A tady je výstup:
Jsem v metodě třídy TridaB
Jsem v metodě třídy TridaA
Fatal error: Call to undefined function:
inicializuj() in /var/www/priklad.php on line
Co je špatně? Vytváříme objekt třídy TridaB a voláme jeho metodu delejNecoJineho(). Tato metoda vypíše hlášení a staticky (?) zavolá metodu delejNeco() třídy TridaA. Také tato metoda vypíše svůj text. Protože jsme ji však volali zevnitř jiné metody konkrétního objektu ( $objekt_b), je v její proměnné $this uložen odkaz na něj. Pak ale musí volání $this->inicializuj() nutně zkolabovat, protože objekt v $this patří k třídě TridaB, a ta žádnou metodu s názvem inicializuj nedefinuje. Vidíte, jak může být celá záležitost nebezpečná.
Abyste se vyhnuli nepříjemným chybám, měli byste tuto konstrukci používat jen pro volání předefinované metody z metody předefinovávající.
Polymorfizmus
Polymorfizmus je další příjemnost zavedená objektově orientovaným programováním. Spočívá v tom, že používáte pojmenované akce (rozumějte volání metod) objektů, každý objekt má však činnost implementovanou podle svých potřeb. Možná to zní složitě, ale až si vše ukážeme na příkladu, poznáte, že o žádnou vědu nejde.
Vraťme se znovu k našemu Menu. Vymysleli jsme další rozšíření stávajících tříd – budeme vytvářet třídu PodrobneMenu, která ke každé položce menu přidá krátký popis a umístí ji do samostatného odstavce.
class PodrobneMenu extends CentrovaneMenu {
var $popisy;
# nový konstruktor
function PodrobneMenu() {
$this->CentrovaneMenu();
}
# vylepšená verze vloz()
function vloz($nazev, $url, $popis = '') {
CentrovaneMenu::vloz($nazev, $url);
$this->popisy[] = $popis;
}
# předefinování zobrazení jedné položky
function zobrazPolozku($x) {
print '<P><A HREF="' . $this->adresy[$x]
. '">' . $this->nazvy[$x] . '</A> - '
. $this->popisy[$x] . '</P>';
}
}
$podrobne_menu = new PodrobneMenu();
$podrobne_menu->centrovat();
$podrobne_menu->vloz('Úvod', 'index.html', 'Úvodní slovo autora');
$podrobne_menu->vloz('Jedna', '1.html', '1. kapitola');
$podrobne_menu->vloz('Dvě', '2.html', '2. kapitola');
Uvedený kód nové třídy nepoužívá žádné nové konstrukce – všechny prvky syntaxe jsme si už popsali. Kde se tedy skrývá ten polymorfizmus? Jeho účinek nalezneme v metodě zobraz() třídy Menu(). Tam se v cyklu for volá akce (metoda) $this->zobrazPolozku() a díky polymorfizmu se provede pokaždé její „nejčerstvější“ verze; v třídách Menu a CentrovaneMenu je to metoda Menu::zobrazPolozku(), ale v třídě PodrobneMenu je to její vlastní metoda PodrobneMenu::zobrazPolozku() – každý objekt si akci daného jména implementuje podle svého.
V uvedeném postupu můžeme jít ještě dál. Můžeme si třeba nadefinovat další třídy pro tabulky, formuláře a ostatní prvky WWW stránek, a pro každou z nich naprogramovat její vlastní metodu zobraz(). Objekty pak můžeme skladovat v polích nebo ve speciálních objektech (kontejnerech) a díky polymorfizmu na ně v cyklu hromadně volat zobraz(), čímž si vynutíme pokaždé jinou (ale patřičnou) odezvu. Pilný čtenář si jistě všechno promyslí a vyzkouší.
Poznámka: Pojem polymorfizmu se v jazycích C++ nebo Pascalu objevuje ve spojitosti s tzv. virtuálními metodami. V PHP je celá situace snadnější: „virtuálními“ jsou jednoduše úplně všechny metody.
Pokračování příště…