PHP_04 - Objekty, třídy a dědičnost
1. Obsah
- Obsah
- Úvod
- Definice a instance třidy
- Metody
- Konstruktor
- Řízení přistupu k metodám a proměnným
- Skládání
- Dědičnost
- Úkol
- Zdroje a citace
2. Úvod
Na úvod si řeknem něco málo teorie o objektově orientovaném programování (dále jen OOP), abychom rozuměli základním termínům jako je objekt, třída, atribut, metoda a podobně.
Co je to OOP a proč by mě mělo zajímat? Je to jeden ze stylů (neboli paradigma) imperativního (neboli „podmínky a cykly“) programování, jež pracuje se základní jednotkou zvanou objekt. Objekt je něco, co dokáže udržovat svůj stav a interagovat s okolím zasíláním a přijímáním zpráv. OOP vzniklo jako reakce na stále se zvyšující složitost programů s cílem usnadnit jejich psaní.
Člověka by mělo zajímat hlavně proto, že dnes je to majoritní styl programování, a pokud chce použít některé knihovny jako je Zend Framework (popis na Wikipedii), či dnes stále populárnější Nette (opět popis na Wikipedii), tak se prostě bez alespoň základních znalostí v OOP neobejde.
Přesuňme se teď trošku víc k programování. 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í (interfaces). Je dobré si uvědomit, že zapouzdřenost je jednou ze základních vlastností objektů - nevidíme a nevíme, co se děje uvnitř, což nám dovoluje dosahovat maximálního nadhledu (abstrakce).
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.
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 třídu na škole. 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 CStudent
.
3. Definice a instance třidy
Příklad definice třídy:
Název třídy může být buď posloupnost znaků vyhovujících regulárnímu výrazu [a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*
(řečí smrtelníků: písmeno anglické abecedy, podtržítko či nějaký znak z horní řady ASCII následovaný žádným nebo více písmeny anglické
abecedy nebo čísly, podtržítky či znaky z horní řady ASCII), nebo proměnná. Jedná-li se o proměnnou, převede se nejdříve obsah této proměnné
na řetězec a jako název třídy se vezme takto získaná hodnota viz Přiklad 3.2.
Příklad 3.1:
class CStudent {
var $prijmeni, $jmeno;
var $rocnik = 4, $skupina, $kat_cislo;
}
Příklad 3.2:
$trida = "Trida";
$objekt = new $trida;
echo get_class($objekt); // vytiskne Trida
}
Vězte, že funkce get_class()
vrací název třídy daného objektu.
Příklad instance (zjednodušený jste již mohli videt v Přikladu 3.2):
Příklad 3.3:
$student = new CStudent; // $student je instancí třídy CStudent
Přístup k proměnným objektu $student
:
(ročník jsme inicializovali již v definici třidy CStudent
)
Příklad 3.4:
// Naplnime studenta Jara Cimrman z katalogovym cislem 13 a patricim do skupiny AJ1
$student->jmeno = "Jára";
$student->prijmeni = "Cimrman";
$student->skupina = "AJ1";
$student->kat_cislo = 13;
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?
4. Metody
Jak jsme již zmínili v kapitole 2., tak Metody
jsou vlastne Funkce
třídy.
Příklad 4.1: - vytvoření metody
class CStudent {
var $prijmeni, $jmeno;
var $rocnik = 4, $skupina, $kat_cislo;
function tisk () {
echo $this->prijmeni." ".$this->jmeno." ze ".$this->rocnik." ".$this->skupina;
} // END OF: function Tisk ()
}// END OF: class CStudent
Seznam a vysvětlení nových použitých chování a vlastností.
$this
- je speciální objekt, který odkazuje na aktuální instanci, které je zpráva posílána. Pokud tedy budeme mít objekt třídyCStudent
uložený v proměnné$objekt
a zavoláme metodutisk
,$this
v metodětisk
ukazuje na stejný objekt jako$objekt
.- Všiměte si, že počáteční písmeno názvu třidy mame velké "C" a název metody tisk začíná malým "t". Je to opět obecné nepsané pravidlo, které se snažte dodržovat, tedy, že názvy tříd začínají velkým písmenem (snadno je pak v kódu poznáme) a názvy metod začínají malým písmenem. Jak s mezerami? V názvech metod používáme znak "_" (podtržítko) a v názvech tříd nepoužíváme, píšeme je jako SMSky bez mezer, tedy "NazevMojiNoveTridy". Je dobré si tyto pravidla osvojit, používají se spolu s namespaces ve většině velkých programátorských týmech a firmách.
Příklad 4.2: - volání metody
$student = new CStudent;
$student->jmeno = "Jára";
$student->prijmeni = "Cimrman";
$student->skupina = "H";
$student->kat_cislo = 13;
// zavoláme metodu tisk
$student->tisk();
Příklad 4.3: - metoda s parametrem
class CStudent {
var $prijmeni, $jmeno;
var $rocnik = 4, $skupina, $kat_cislo;
function tisk ($jeden_radek = true) {
if ($jeden_radek == true) {
echo $this->prijmeni." ".$this->jmeno." ze ".$this->rocnik." ".$this->skupina;
} // END OF: if ($jeden_radek == true)
else {
echo $this->rocnik." ".$this->skupina."<br />\r\n";
echo $this->prijmeni." ".$this->jmeno;
} // END OF: viceradkovy vypis
} // END OF: function tisk ($jeden_radek = true)
} // END OF: class CStudent
Ještě nám chybí jeden speciální druh metody a to je statická metoda. Tyto metody nevyžadují instancování celé třídy
(tedy zavolání na třídu new
, ale mohou byt volány "jen tak" samy o sobě. Ke statickým metodám se nepřistupuje pomocí operátorui
->
ale pomocí "čtyřtečky" ::
, tedy dvou znaku dvojtečeky za sebou. Ale nejlepší to zase bude cele ukázat na příkladě.
Tak si představte, že potřebujeme třídu CMatematika
, která bude obsahovat základní matematické funkce jako sinus, kosinus, konstantu pi
a podobně.
Příklad 4.4: - staticka metoda
class CMatematika {
static public $pi = 3.141592657; // verejna promena
static public function getPi () {
// funkce vrati hodnotu konstanty Pi
return 3.141592657; // POZOR nelze volat $this->get_pi, trida neni instancovana!
} // END OF: public function getPi ()
static public function sin($cislo) {
// funkce vrati hodnotu sinus z $cisla, ktere musi byt uvedeno v radianech
return sin($cislo);
} // END OF: public function sinus($cislo)
static public function cos($cislo) {
// funkce vrati hodnotu cosinus z $cisla, ktere musi byt uvedeno v radianech
return cos($cislo);
} // END OF: public function sinus($cislo)
} // END OF: class CMatematika
// a ted přímo k vlastnímu použití
echo 'Hodnota PI je: '.CMatematika::getPi()."<br />\n";
echo 'Hodnota sin(60°) je: '.CMatematika::sin(deg2rad(60))."<br />\n"; // a cosinus obdobne ...
echo 'Jiny zpusob volani PI'.CMatematika::$pi."<br />\n"; // staticke volani promene $pi
Seznam a vysvětlení nových použitých chování a vlastností.
- Jak vidíte nikde jsem nepoužil instancování třídy pomocí
new
a přesto to funguje, jen musíme metody volat staticky pomocí již zmíněného operátoru "::
". - Metody, které považuji za statické, jsem označil pomocí
static
. - Nezapomentě, že pokud voláme metody staticky, tak se nepoužije konstruktor!
- Staticky můžeme volat ne jen metody, ale i proměnné a konstanty.
- Taktéž si všimněte, že pro navrácení hodnoty PI jsem použil v názvu u obslužné metody prefix
get
. Je to opět jeden ze zvyků, že metody, které něco varcí maji prefixget
a metody, co neco nastavují, přiřazují a pdoobně, maji prefixset
.
5. Konstruktor
Instancování (aneb vytvoření instance třídy, aneb vytvoření objektu dle třídy) a inicializace se dají smrsknout do jednoho volání. K takovým účelům slouží konstruktor. Pomocí konstruktoru můžeme například ihned po vytvoření instance inicializovat hodnoty počáteční hodnoty proměnných, což se může velice hodit.
Příklad 5.1: - metoda s parametrem
class CStudent {
// "globalni" promene tridy
var $prijmeni, $jmeno;
var $rocnik, $skupina, $kat_cislo;
// metoda volana pri instancovani tridy -> predani parametru do globalnich parametru tridy
function __construct($prijmeni, $jmeno, $rocnik, $skupina, $kat_cislo) {
$this->prijmeni = $prijmeni;
$this->jmeno = $jmeno;
$this->rocnik = $rocnik;
$this->skupina = $skupina;
$this->kat_cislo = $kat_cislo;
} // END OF: function __construct($prijmeni, $jmeno, $rocnik, $skupina, $kat_cislo)
// metoda pro vypis obsahu promenych
function tisk ($jeden_radek = true) {
if ($jeden_radek == true) {
echo $this->prijmeni." ".$this->jmeno." ze ".$this->rocnik." ".$this->skupina;
} // END OF: if ($jeden_radek == true)
else {
echo $this->rocnik." ".$this->skupina."<br />\r\n";
echo $this->prijmeni." ".$this->jmeno;
} // END OF: viceradkovy vypis
} // END OF: function tisk ($jeden_radek = true)
} // END OF: class CStudent
Seznam a vysvětlení nových použitých chování a vlastností.
- Metoda
__construct
je taková zvláštní, speciální. Říká se jí konstruktor a je zavolána hned po vytvoření objektu operátoremnew
. Konstruktoru se právě předají ty parametry, které jsou v závorkách při vytváření pomocínew
, jak je vidět v Příkladu 5.2. - Všechny vlastnosti, bez kterých by objekt neměl smysl, by se měly inicializovat v konstruktoru.
- V PHP 4.x se místo metody s názvem
__construct
používala jako konstrukt metoda, která se jmenovala stejně jako třída. (takže v našem případě by to bylo:function CStudent($prijmeni, $jmeno, $rocnik, $skupina, $kat_cislo)
. Od tohoto chování se ve verzi PHP 5.0 upustilo, ale kvůli zpětně kompatibilitě je stále podporováno - ovšem jak dlouho, je otázkou.
Příklad 5.2: - ukázka funkce konstruktoru
$student1 = new CStudent("Cimrman", "Jára", 4, "AJ1", 13);
$student1->Tisk(true); // vypse: Cimrman Jára ze 4 AJ1
$student2 = new CStudent("Carda", "Retarda", 4, "AJ2", 13);
$student2->Tisk(false); /* vypise: 4 AJ2
Carda Retarda */
6. Řízení přistupu k metodám a proměnným
Věcí, která se by se nám nemusela líbit je, že vlastnost například $jmeno
může změnit kdokoli odkudkoli a stačí mu k tomu
jenom reference na objekt. Člověk by neměl věřit cizímu kódu a už vůbec ne svému, a tak budeme chtít přístupy ke $jmeno
(ale i dalším atributům objektu) nějak ochránit. PHP od verze 5.0 zná celkem tři typy ochrany (proměnných, tedy atributů a také metod, tedy funkcí):
public
- veřejně přístupné; aneb žádná ochrana, výchozí stavprotected
- chráněné; k vlastnosti mají přístup vlastní instance a instance potomků třídy (něco o dědění bude dále v Kapitole 7.)private
- jen a jen moje; k tomu se nedostane nikdo jiný než objekty dané třídy
Tato tři klíčová slova se při definici vlastnosti používají místo var
, takže to může vypadat třeba takhle:
Příklad 6.1:
protected $prijmeni, $jmeno; // nyni je $jmeno a $prijmeni pristupne pouze v telech metod tridy CStudent
Stejně jako s vlastnostmi a jejich ochranou je to i u metod. Akorát že tam klíčová slova značící přístup nenahrazují slovo
function
, nýbrž se umisťují před něj.
Příklad 6.2:
public function __construct($$prijmeni, $jmeno, $rocnik, $skupina, $kat_cislo) { ... }
public function tisk() { ... }
Doporučuji nikdy nepoužívat var
(jak již bylo naznačeno, je to relikt z PHP 4.x; při definování vlastností tedy používat
pouze klíčová slova řízení přístupu) a u funkcí vždy uvádět, jakou ochranu mají (bůhví, co si dokáží vývojáři PHP usmyslet do dalších verzí,
třeba nakonec v PHP 6 bude výchozí private
).
7. Skládání
Řekněme že jsme se rozhodli odlišit studenta maturanta od ostatních studentů. Existují dva způsoby jak delegovat (předávat) nějaké navržené (doporučené/funkční) schéma mezi objekty a to:
- Skládání objektů
- Specializace (dědění) objektů - Kapitola 8.
Příklad 7.1: - skládání a interface
Skládání objektů spočívá v tom, že jeden objekt osahuje referenci na jiný objekt (takže danému referencovanému objektu může zasílat zprávy). Takže
budeme potřebovat novou metodu tisk
, která zohlední našeho případného maturanta, nebo nematuranta. Přidání nové tiskové metody obnáší
vytvořit novou třídu, která podporuje daný protokol. Takže si za tímto účelem vytvoříme interface třídy CTiskar
.
interface CTiskar {
public function tisk($jeden_radek); // pozor parametrum metod nesmime prirazovat defaultni hodnoty!
// ani zde nesmime defninovat clenske promenne
} // END OF: interface CTiskar
Seznam a vysvětlení nových použitých chování a vlastností.
- K jasné definici protokolu slouží tzv. "interface", česky rozhraní.
- Definice interface je podobná té třídní, proto neuvádíme klíčové slovo
class
, nýbržinterface
a u metod neuvádíme tělo (pouze řízení přístupu, název a seznam parametrů). - Pomocí interface nelze definovat zprávy pro práci s vlastnostmi (získání, přiřazení hodnoty), ale pouze metody.
Třídy, které se zavazují k možnostem zasílat jim zprávy daného protokolu, rozhraní tzv. „implementují“:
Příklad 7.2: - implements
class CStudujici implements CTiskar {
// verejnte pristupne promene
public $prijmeni, $jmeno;
public $rocnik, $skupina, $kat_cislo;
public function __construct($prijmeni, $jmeno, $rocnik, $skupina, $kat_cislo) {
// promene predany z interface tridy
$this->prijmeni = $prijmeni;
$this->jmeno = $jmeno;
$this->rocnik = $rocnik;
$this->skupina = $skupina;
$this->kat_cislo = $kat_cislo;
} // END OF: function __construct($prijmeni, $jmeno, $rocnik, $skupina, $kat_cislo)
// metoda pro vypis obsahu promenych
public function tisk ($jeden_radek = true) {
if ($jeden_radek == true) {
echo $this->prijmeni." ".$this->jmeno." ze ".$this->rocnik." ".$this->skupina;
} // END OF: if ($jeden_radek == true)
else {
echo $this->rocnik." ".$this->skupina."<br />\r\n";
echo $this->prijmeni." ".$this->jmeno;
} // END OF: viceradkovy vypis
} // END OF: function tisk ($jeden_radek = true)
} // END OF: class CStudujici implements CTiskar {
Seznam a vysvětlení nových použitých chování a vlastností.
- Takže jak vidíte v definici třídy
CStudujici
, což je ještě neodmaturovany student, jsme se zavázali pomocíimpelemnts CTiskar
používat protokol třidyCTiskar
, která se nám stará protokol na výstupu tištěných hodnot. - Třída samozřejmě může implementovat víc než z jedné třídy, tyto třídy se pak od sebe oddělují na řádku čárkou.
Příklad 7.3: - vlastní skládání
class CStudent {
private $tiskar;
public function __construct(CTiskar $tiskar) {
$this->tiskar = $tiskar;
} // END OF: public function __construct(CTiskar $tiskar)
// metoda pro vypis obsahu promenych
function tiskni ($jeden_radek = true) {
return $this->tiskar->tisk($jeden_radek);
} // END OF: function tisk ($jeden_radek = true)
} // END OF: class CStudent
Seznam a vysvětlení nových použitých chování a vlastností.
- Konstruktor nám již nepřijímá jmeno, příjmení studenta a podobně, místo toho dostává
$tiskar
e. - Uvedeni "typu" CTiskar před názvem parametru je tzv. type hinting.
PHP tím říkáme, že má ověřit, že se opravdu jedná o
CTiskaře
a ne třeba o řetězec, číslo nebo pole. Pokud znáte nějaký staticky typovaný jazyk, nemyslete si, že byste type hintingem nahradili typovou analýzu v době kompilace, vše probíhá za běhu. Type hinting PHP pouze napoví ("hint" česky znamená naznačit, napovědět, náznak něčeho), po čem by se mělo koukat a co by mělo kontrolovat. - Také si všiměte že
$tiskar
jeprivate
, aby k němu mimo třídu neměl nikdo jiný přistup - A vlastní skládání je pak patrné z těla metody
tiskni
a to:$return $this->tiskar->tisk($jeden_radek);
A jak můžeme potom snadno odvodit novou třídu CMaturant
se stejnym interface jako CTiskar
krásně vidíme na dalším příkladu.
Příklad 7.4: - jiný způsob implmentace metody tisk
class CMaturant implements CTiskar {
// verejnte pristupne promene
public $prijmeni, $jmeno;
public $rocnik, $skupina, $kat_cislo;
public $odmaturoval; // true nebo false jestli odmaturoval;
public $odmaturoval_arr = Array(true => 'Odmaturoval', false => 'Nedomaturoval');
public function __construct($prijmeni, $jmeno, $rocnik, $skupina, $kat_cislo) {
// promene predany z interface tridy
$this->prijmeni = $prijmeni;
$this->jmeno = $jmeno;
$this->rocnik = $rocnik;
$this->skupina = $skupina;
$this->kat_cislo = $kat_cislo;
$this->odmaturoval = $this->_maturita();
} // END OF: function __construct($prijmeni, $jmeno, $rocnik, $skupina, $kat_cislo)
protected function _maturita() {
// metoda nahodne rozhodne jestli student odmaturoval nebo ne a vrati to jako @boolean
return (rand (1, 10) < 6);
} // END OF: protected function _maturita() {
// metoda pro vypis obsahu promenych
public function tisk ($jeden_radek = true) {
if ($jeden_radek == true) {
echo $this->prijmeni." ".$this->jmeno." ze ".$this->rocnik." ".$this->skupina.
" [{$this->odmaturoval_arr[$this->odmaturoval]}]";
} // END OF: if ($jeden_radek == true)
else {
echo $this->rocnik." ".$this->skupina."<br />\r\n";
echo $this->prijmeni." ".$this->jmeno." [{$this->odmaturoval_arr[$this->odmaturoval]}]";
} // END OF: viceradkovy vypis
} // END OF: function tisk ($jeden_radek = true)
} // END OF: class CMaturant implements CTiskar
Seznam a vysvětlení nových použitých chování a vlastností.
- Vznikly nám nové členské proměnné
$odmaturoval
a$odmaturoval_arr
, na nich snad nemusím již nic vysvětlovat. - Zajímavější je pak metoda
$protected function _maturita()
, která je jednakprotected
, takže je přístupná jen uvnitř naší třídyCMaturant
a pak nám náhodně generuje jestli student odmauroval nebo ne:-) - Také si všiměte metoda
_maturita
se vola v kontruktoru a plní nám tam proměnou$odmaturoval
, která je přístupná i na vnějším rozhranní třídy. - A proč má metoda
_maturita
na počátku podržítko "_"? Opět se jedná o zvyk, aby se snadno a hned na první pohled odlišily věřejné a neveřejné metody. Velice doporučuji toto pravidlo dodržovat, přispíváte k přehlednosti kódu a používá se to opět na všech větších projektech.
A jak se to celé používá v praxi?
Příklad 7.5: - skládání v praxi
$maturantik = new CMaturant("Cimrman", "Jára", 4, "AJ1", 13);
$obycejny_student = new CStudujici("Petr", "Skočdostroje", 3, "AJ2", 15);
$student1 = new CStudent($maturantik);
$student1->tiskni(); // vytiskne nam Jaru Cimrmana s tim jestli odmaturoval nebo ne
echo '<br />';
$student2= new CStudent($obycejny_student);
$student2->tiskni(false); // vytiskne nam Petra Skocdopole (obycejneho studenta) a jeste s odradkovanim.
8. Dědičnost
A nyní se již můžeme podívat i na dědičnost. Vytvoříme novou třídu, která zdědí některé atributy (proměnné) a
metody (funkce) z výchozí třídy CStudnet
v Příkladu 5.1. A neb již za sebou určitě máte Kapitolu 7., tak to vše
zapíši v jednom příkladě a pod ním si rozebereme všechny nové syntaktické prvky a i některé navic:-).
Příklad 8.1: - dědičnost (extends)
abstract class CStudent {
// "globalni" promene tridy
public $prijmeni, $jmeno;
public $rocnik, $skupina, $kat_cislo;
public function tiskni($jeden_radek = true){
return $this->_tisk($jeden_radek);
}
abstract protected function _tisk($jeden_radek = true); // funkce tisk bude (musi byt!)
// implementovana v potomkovi
} // END OF: abstract class CStudent
final class CMaturant extends CStudent {
// verejnte pristupne promene
public $odmaturoval; // true nebo false jestli odmaturoval;
public $odmaturoval_arr = Array(true => 'Odmaturoval', false => 'Nedomaturoval');
public function __construct($prijmeni, $jmeno, $rocnik, $skupina, $kat_cislo) {
// promene predany z interface tridy
$this->prijmeni = $prijmeni;
$this->jmeno = $jmeno;
$this->rocnik = $rocnik;
$this->skupina = $skupina;
$this->kat_cislo = $kat_cislo;
$this->odmaturoval = $this->_maturita();
} // END OF: function __construct($prijmeni, $jmeno, $rocnik, $skupina, $kat_cislo)
protected function _maturita() {
// metoda nahodne rozhodne jestli student odmaturoval nebo ne a vrati to jako @boolean
return (rand (1, 10) < 6);
} // END OF: protected function _maturita() {
// metoda pro vypis obsahu promenych
protected function _tisk($jeden_radek = true) {
if ($jeden_radek == true) {
echo $this->prijmeni." ".$this->jmeno." ze ".$this->rocnik." ".$this->skupina.
" [{$this->odmaturoval_arr[$this->odmaturoval]}]";
} // END OF: if ($jeden_radek == true)
else {
echo $this->rocnik." ".$this->skupina."<br />\r\n";
echo $this->prijmeni." ".$this->jmeno." [{$this->odmaturoval_arr[$this->odmaturoval]}]";
} // END OF: viceradkovy vypis
} // END OF: function _tisk ($jeden_radek = true)
} // END OF: class CMaturant extends CStudent
class CStudujici extends CStudent {
public function __construct($prijmeni, $jmeno, $rocnik, $skupina, $kat_cislo) {
// promene predany z interface tridy
$this->prijmeni = $prijmeni;
$this->jmeno = $jmeno;
$this->rocnik = $rocnik;
$this->skupina = $skupina;
$this->kat_cislo = $kat_cislo;
} // END OF: function __construct($prijmeni, $jmeno, $rocnik, $skupina, $kat_cislo)
protected function _tisk($jeden_radek = true) {
if ($jeden_radek == true) {
echo $this->prijmeni." ".$this->jmeno." ze ".$this->rocnik." ".$this->skupina;
} // END OF: if ($jeden_radek == true)
else {
echo $this->rocnik." ".$this->skupina."<br />\r\n";
echo $this->prijmeni." ".$this->jmeno;
} // END OF: viceradkovy vypis
} // END OF: function _tisk ($jeden_radek = true)
} // END OF: class CStudujici extends CStudent
Seznam a vysvětlení nových použitých chování a vlastností.
- Určite jste si u třídy
CStudent
všimli slůvkaabstract
, které má obdobný význam jakoimplements
- tedy jedná se o jakýsi předpis třidy, s tím rozdílem, že již můžeme inicializovat členské proměnné a podobně. - V třídě
CStudent
máme syntaktický výrazabstract
také před metodou_tisk()
, v tomto případě to má vyznam ten, že metoda není implementována v této třídě, ale bude (povinně) implememtnována v každém potomkovi. - Termín
final
u třídyCSMaturant
nám říká, že tato třída nejde dále dědit. Dá se to využít jako bezpečnostní prvek, aby nám někdo nepodědil nežádoucí třídu a pdoobně. - Proč v každěm potomkovi znovu a znovu vytvářím metodu
function __construct()
v "plném" rozsahu? Je to z důvodu, že při instancování zděděné metody (potomka), nedochází k volání konstruktoru otcovské třídy (v našem případě třídyCStudent
). Pozor toto chování není identické všem jazykům jako C++ a pod. - Třída
CMaturant
zdědila všechny vlastnosti a metody třídy CStudent díkyextends CMaturan
, (extends
znamená z angličtiny něco jako rozšiřujíci). - V tomto příkladu se objevuje jedna ze základních vlastnoctí a výhod OOP a to je polymorfismus. Uhodnete kde (čím) se tato vlastnost
v tomto příkladě projevuje? Dobrá, napovím: Je to na metodě
_tisk()
. Krásně tu vidíme, že každý potomek třídyCStudent
si může metodutisk()
implementovat po svém (podle svých pravidel), což je jedna z velice přínosných vlastností OOP. - Ješte se můžete potřebovat volat metodu předka, k tomu slouži notace
parent::_tisk(true);
- Při vícenásobném dědění (například třída C dědí z třídy B a třída B dědí z třídy A) můžete přistupovat k vlastnostem jen otce, nikoli praotce,
prapraotce a podobně (ale zase pozor, toto nelze genearlizovat na OOP všech jazyků, například v Jave, toto neplatí). V našem příkladě s A, B a C je to tedy
přístup z C jen k vlastnostem a metodám třídy C a B, nikoli i A! Možná vás napadne jak potom rozlišit některé třeba stejně pojmenované vlastnosti? Dobrá
otázka! Slouží k tomu potom mimo odkazu
$this->neco();
, také$self->neco();
, kdy$self
má nejčerstvější úroveň (v našem příkladě ke zdrojům třídy C). - Tak již poslední vlastnost k OOP, slibuji:-). V některýh specifických případech potřebujeme v průběhu kompilace skriptu postupně objekty "likvidovat"
(
unset($ukazatel_na_objekt);
), kvůli pamětovým nárokům aplikace v čase a podobně. A v tom případě potřebujeme, při záníku objektu zavírat soubory, ukončovat sezení k databázovým uložištím, aby nám nedošly systémové zdroje. Jak na to? Je to celkem jednoduché, vedle konstruktoru (public function __construct();
) totiž existuje i destruktor (public function __destruct();
) a ten se volá při zániku objektu, resp. když ho PHP garbage collector uvolňuje z paměti. O PHP garbage collectoru bychom tu toho mohli napsat hodně, ale prostě to zkrátím a řekněme, že od PHP 5.3 funguje s každou verzi PHP lépe a lépe...
A jak třídy z Příkladu 8.1 použijeme vidíte na následujím příkladě.
Příklad 8.2: - dědičnost v praxi
$student1 = new CStudujici("Cimrman", "Jára", 4, "H", 13);
$student1->tiskni();
echo '<br />'; // novy radek pro dalsi volani druhwe tridy
$student2 = new CMaturant("Carda", "Retarda", 4, "G", 13);
$student2->tiskni(false); // odradkovany vypis
9. Úkol
- Napište skript PHP, který vypočíta BMI index a použijte objekt, vhodné atributy a metodu na výpočet indexu.
- Samozřejmě můžete využít minulého úkolu
10. Zdroje a citace
- ACHOUR, Mehdi, Friedhelm BETZ, Antony DOVGAL, Nuno LOPES, Hannes MAGNUSSON, Georg RICHTER, Damien SEGUY a Jakub VRÁNA. PHP: PHP Manual. [online]. [cit. 2012-08-21]. Dostupné z: www.php.net/manual/
- OOP v PHP. In: Programujeme.com [online]. [cit. 2012-08-24]. Dostupné z: http://programujte.com/clanek/2009113001-oop-v-php/
- VRÁNA, Jakub. Kniha 1001 tipů a triků pro PHP [online]. 1. vyd. 2010 [cit. 2012-08-21]. Dostupné z: http://php.vrana.cz/kniha-1001-tipu-a-triku-pro-php.php
- BURDA, Michal. PHP v objetí objektů. In: ROOT.CZ [online]. [cit. 2012-08-24]. Dostupné z: http://www.root.cz/clanky/php-v-objeti-objektu-1/
- ANTHARES. Object Oriented PHP Best Practices. In: Stackoverflow [online]. [cit. 2012-08-24]. Dostupné z: http://stackoverflow.com/questions/2407807/object-oriented-php-best-practices
- Poznámky neznámého učitele ze SPŠ Zlín :-)