Hlavní navigace

PHP v objetí objektů (4)

6. 8. 2001
Doba čtení: 4 minuty

Sdílet

Dnes si podrobně probereme význam konstrukce NazevTridy::NazevMetody(). Čeká nás i zmínka o polymorfizmu.
Statická volání metod

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ší.

CS24 tip temata

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

Autor článku

Michal Burda vystudoval informatiku a aplikovanou matematiku a nyní pracuje na Ostravské univerzitě jako odborný asistent. Zajímá se o data mining, Javu a Linux.