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ě…