14. díl - Interface (rozhraní v PHP)

PHP Objektově orientované programování Interface (rozhraní v PHP) American English version English version

V minulém tutoriálu o objektově orientovaném programování v PHP jsme si vyzkoušeli praktické znalosti OOP na příkladu s počítadlem návštěv. Dnes zas načneme novou teorii, kterou bude rozhraní. Budeme pokračovat v našem příkladu s lidmi.

Interface (rozhraní)

Rozhraním se v programování myslí veřejné metody, které nám třída vystavuje zvenčí. Pomocí tohoto rozhraní s instancí třídy poté komunikujeme.

Rozhraní je však možné definovat samostatně, mimo třídu. Takové rozhraní neobsahuje implementaci, ale pouze hlavičky metod. Je to zkrátka jen rozhraní :) Když si takové rozhraní vytvoříme, můžeme ho nechat implementovat v nějaké třídě. Zkusme si to.

Rozhraní si vytvoříme ve složce tridy, i když třídou vlastně není. Půjde o rozhraní programátora. Založte si nový soubor s názvem Programator.php. Do něj vložte následující obsah:

interface Programator
{
    public function programuj();
}

Rozhraní Programator obsahuje jednu metodu programuj(). Mohlo by jich samozřejmě obsahovat více a metody by mohly mít i parametry. Pro naše účely to však bohatě stačí.

Přesuňme se do naší třídy Javista.php. Pro úplnost zopakuji jak její kód vypadá:

class Javista extends Clovek
{

    public $ide;

    public function __construct($jmeno, $prijmeni, $vek, $heslo, $ide)
    {
        $this->ide = $ide;
        parent::__construct($jmeno, $prijmeni, $vek, $heslo);
    }

    public function pozdrav()
    {
        echo('Hello world! Jsem ' . $this->jmeno);
    }

    public function programuj()
    {
        echo("Programuji v {$this->ide}...");
    }

}

Třída dědí z třídy Clovek, volá rodičovský konstruktor, přepisuje metodu pozdrav() a přidává novou metodu programuj().

My již víme, že v PHP můžeme dědit pouze z jedné třídy. Můžeme však zároveň implementovat i libovolný počet rozhraní. V deklaraci třídy pomocí klíčového slova implements určíme, že implementuje rozhraní Programator.

class Javista extends Clovek implements Programator

Co se týká názvosloví, třídy se dědí, rozhraní se implementují. Je to logické, jelikož dědění třídu obohatí i novou implementací, interface třídě pouze přikáže, že má obsahovat nějaké metody. Rozhraní můžeme ve třídě implementovat kolik chceme, oddělují se čárkou.

V index.php si vytvoříme nějakého Javistu:

$javista = new Javista('Tomáš', 'Marný', 28, 'mamradsvyheslo', 'NetBeans');

Když aplikaci zkusíme spustit, vše funguje stejně dobře, jako předtím. Nyní se přesuňte do třídy Javista a metodu programuj() zakomentujte. Když znovu přejdeme na index, zobrazí se nám následující chybová hláška:

Rozhraní/interface v PHP

Hláška nám sděluje, že třída Javista implementuje rozhraní a přitom v implementaci nemá všechny metody, které toto rozhraní obsahuje. Metodu opět odkomentujeme.

Význam rozhraní

Rozhraní má v programování hned několik významů.

Předpis metod

Jak jsme si nyní vyzkoušeli, umožňuje předepsat třídě jaké metody musí obsahovat. Podobně jako např. modifikátory viditelnosti (private) je to další z nástrojů, který nám umožňuje hlídat správnou funkčnost kódu.

Zjištění přítomnosti rozhraní

Vraťme si do index.php naše pole lidí s několika instancemi Javistů a lidí. V cyklu tyto instance projedeme a necháme je pozdravit. Pokud je daná instance programátor, necháme ho programovat. Všimněte si, že říkám programátor, ne Javista. Můžeme se totiž zeptat zda instance implementuje určité rozhraní:

$lide = array();
$lide[] = new Clovek('Karel', 'Novák', 30, 'felix15');
$lide[] = new Javista('Jan', 'Nový', 24, 'mamradsvykolo', 'Eclipse');
$lide[] = new Clovek('Josef', 'Nový', 50, 'password');
$lide[] = new Javista('Tomáš', 'Marný', 28, 'mamradsvyheslo', 'NetBeans');
$lide[] = new Clovek('Marie', 'Nová', 32, 'tohlen3uhodn3s');

foreach ($lide as $clovek)
{
        echo($clovek . '<br />');
        if ($clovek instanceof Programator)
        {
                $clovek->programuj();
                echo('<br />');
        }
}

Aplikaci spustíme:

Instanceof a rozhraní v PHP

Ve výsledku vidíme, že jsme všechny lidi vypsali a pokud s člověkem můžeme pracovat jako s programátorem, tak ho necháme i programovat. Ke zjištění přítomnosti rozhraní jsme použili operátor instanceof.

Tato technika je velmi zajímavá a umožňuje nám ptát se jen zda objekt v proměnné obsahuje jen nějakou část, kterou zrovna potřebujeme. Je nám docela jedno, jestli je v proměnné Javista nebo programátor v PHP, děrných štítcích a podobně. Zajímá nás zkrátka jestli objekt vystavuje rozhraní programátor.

Zjištění typu proměnné

S instanceof vlastně zjišťujeme typ instance v proměnné. Můžeme se jednoduše ptát na typ instance v proměnné jen s využitím OOP. Bez instanceof bychom si museli na javistovi např. definovat nějakou konstantu TYP_CLOVEKA s hodnotou "programator". A kdybychom chtěli, aby byl zároveň programátor a šachista, opět bychom měli problém. Takto jen implementujeme další rozhraní.

Když se ptáme na přítomnost rozhraní, nemusíme uvést pouze interface, ale klidně třídu. Kód by fungoval podobně i s touto podmínkou, avšak volal by pouze instance třídy Javista:

if ($clovek instanceof Javista)

Samotná třída se tedy chová také jako rozhraní. Instanceof funguje i při dědění. Pokud několikrát podědíme a zeptáme se pomocí instanceof na nějakého z předků, bude podmínka platit.

V silně typovaných jazycích (např. v Javě) má rozhraní ještě mnohem větší význam, v PHP se bez něj v podstatě obejdete a sám se přiznám, že ho příliš nepoužívám. Občas je však vhodné ho použít, zejména pokud pracujeme s nějakou složitější hierarchií tříd.

Pojmenování rozhraní

Na závěr si krátce řekněme i něco o tom, jak se rozhraní pojmenovávají. Pokud se budeme držet konvencí Javy, platí pro pojmenování rozhraní PascalCase, stejně jako u tříd. Existuje konvence, která předepisuje, že by všechna rozhraní v PHP měla končit na "Interface". Měli bychom tedy např. rozhrani ProgramatorIn­terface.

V angličtině se rozhraní často pojmenovávají s koncovkou -able, např. Drawable (něco, co jde vykreslit, předepisuje např. metodu paint()), sortable a podobně. Česky by to bylo asi Vykreslitelny, což také nezní špatně. Programátor je zrovna případ, kde se název rozhraní vymýšlí špatně, možná by se mohlo jmenovat i Programujici.

Příště budeme s rozhraním pokračovat a uvedeme si tzv. abstraktní třídu. Zdrojové kódy naleznete jako vždy ke stažení pod článkem.


 

Stáhnout

Staženo 361x (4.04 kB)
Aplikace je včetně zdrojových kódů v jazyce PHP

 

  Aktivity (3)

Článek pro vás napsal David Čápka
Avatar
Autor pracuje jako softwarový architekt a pedagog na projektu ITnetwork.cz (a jeho zahraničních verzích). Velmi si váží svobody podnikání v naší zemi a věří, že když se člověk neštítí práce, tak dokáže úplně cokoli.
Unicorn College Autor se informační technologie naučil na Unicorn College - prestižní soukromé vysoké škole IT a ekonomie.

Jak se ti líbí článek?
Celkem (13 hlasů) :
4.846154.846154.846154.846154.84615


 



 

 

Komentáře
Zobrazit starší komentáře (10)

Avatar
Martin Konečný (pavelco1998):

Třída implementující nějaký interface říká, že má nějakou funkčnost (něco umí). Pro příklad

interface IMovable
{

        public function move();

}

class Human implements IMovable
{

        public function move()
        {
                echo "step step step...";
        }

}

class Bird implements IMovable
{

        public function move()
        {
                echo "I'm flying...";
        }

}


// nyní je mi jedno, jestli objekt bude instancí třídy Human nebo Bird, protože vím, že daný objekt se umí pohybovat
if ($cond) {
        $obj = new Human();
} else {
        $obj = new Bird();
}

$obj->move();

Dá se to použít i pro zjištění, zda daný objekt danou funkčnost umí

if ($obj instanceof IMovable) {
        // teď 100% víme, že objekt obsahuje metodu move()
}
 
Odpovědět  +1 15.10.2015 17:39
Avatar
loading84
Člen
Avatar
loading84:
if ($obj instanceof IMovable) {
        // teď 100% víme, že objekt obsahuje metodu move()
}

Dobře tenhle kód ma smysl, ale ty dvě funkce se dají odpálit i když to není v rozhraní. Kromě toho podmínka platí

if ($obj instanceof IMovable) platí i když jsem zakomentoval fuknci v rozhraní

interface IMovable
{

    //    public function move();

}

navic IMovable je kličové slovo tak jsem to rozhrani musel přjemenovat na IMove

dále vůbec nevím jak by se zaházelo s atritbuty daného interface.

Abstraktní třídu chápu ale interface prostě ne, hlavně bych čekal že když zakomentuju fukci

public function move();

že mi podmínka

if ($obj instanceof IMovable)

vratí false a to se nestalo.

Nebo bych čekal že se

$obj->move();

neprovede. Ani jedno se nestalo. Interface mi připada pořád zbytečný.

 
Odpovědět 15.10.2015 19:06
Avatar
Odpovídá na loading84
Martin Konečný (pavelco1998):

Tady jde o to, že ty nemusíš vůbec vědět, o jakou třídu jde, ale stačí ti, že implementuje dané rozhraní (které říká, že ta třída musí mít danou metodu/y).

Zkusim trochu jiný příklad. Představ si objekt, který bude někam ukládat data. Můžeš je ukládat např. do databáze MySQL, do textového souboru, do paměti atd. Pak to zjednodušeně může vypadat třeba takhle

interface IDataStorable
{

        public function save($key, $value);

        public function delete($key);

        public function load($key);

}

// jelikož třídy implemetují rozhraní, musí obsahovat všechny tři metody
class MySQLStorage implements IDataStorable
{

        public function save($key, $value)
        {
                mysql_query("INSERT INTO tabulka (...)");
        }

        public function delete($key)
        {
                mysql_query("DELETE FROM tabulka ...");
        }

        public function load($key)
        {
                mysql_query("SELECT data FROM tabulka WHERE $key ...");
        }

}


class FileStorage implements IDataStorable
{
        private $file;

        public function __construct()
        {
                $this->file = fopen("nějaký soubor");
        }

        public function save($key, $value)
        {
                fwrite($this->soubor, $value);
        }

        public function delete($key)
        {
                // něco na smazání ze souboru
        }

        public function load($key)
        {
                // něco na získání dat ze souboru
        }

}


class MemoryStorage implements IDataStorable
{

        private $data = array();

        public function save($key, $value)
        {
                $this->data[$key] = $value;
        }


        public function delete($key)
        {
                unset($this->data[$key]);
        }


        public function load($key)
        {
                return $this->data[$key];
        }

}

define("DEBUG", TRUE);
if (DEBUG) {
        // pro testovací účely
        $storage = new MemoryStorage();
        $storage->save("test", "nějaká hodnota");
} else {
        // ostrá DB
        $storage = new MySQLStorage();
}

// zde v kódu už vůbec nemusím řešit, jestli se jedná o nějaký debug nebo ne
// protože vím, že ať je to objekt pracující s databází MySQL nebo jen s pamětí,
// musí obsahovat tři výše uvedené metody
// takže nemusim dávat hromadu dalších podmínek a snadno použít
echo $storage->load("test");

Je to jen malý hloupý příklad, v reálu by to bylo trochu jinak, ale pro pochopení je to snad dostačující.

 
Odpovědět 15.10.2015 21:11
Avatar
loading84
Člen
Avatar
loading84:

V podstatě je to šablona pro třídy co musí obsahovat za metody. Co jsem se dočetl tak atributy rozhraní neobsahuje. V praxi to podle mě jde i bez rozhraní. Možná je to pro přehlednost ve třídách je lepší když ve třídě kde je více metod(abych na metodu rozhraní nezapomněl). Jde prostě o to aby se neopomněla nějaká metoda.

 
Odpovědět 16.10.2015 12:59
Avatar
loading84
Člen
Avatar
loading84:

Mám takový dotaz, jestli to chápu dobře, tak interface musí mit metody vždy public. Když jsem dal private tak mi to vyhubovalo.

 
Odpovědět 23.11.2015 20:25
Avatar
mv
Člen
Avatar
Odpovídá na loading84
mv:

Ano musí :) Základní myšlenka rozhraní je, že se k němu bude přistupovat zvenčí, privátní metody nemají smysl.

 
Odpovědět 23.11.2015 20:39
Avatar
Petr Šauer
Člen
Avatar
Petr Šauer:

Tak tady jsme se fakt kousnul :-S , jak určím z jaké třídy je metoda "programuj" ?

interface Programator
{
    public function programuj();
}
 
Odpovědět 27. ledna 15:32
Avatar
patrik.valkovic
Šéfredaktor
Avatar
Odpovídá na Petr Šauer
patrik.valkovic:

Jak z jaké třídy? Třída je nad rozhraním. První definuješ rozhraní, potom teprve třídu. Tebe nemusí zajímat, jakou třídu máš, pro tebe je důležité, že implementuje požadované rozhraní.

Odpovědět  +1 27. ledna 15:39
Nikdy neumíme dost na to, abychom se nemohli něco nového naučit.
Avatar
Petr Šauer
Člen
Avatar
Odpovídá na patrik.valkovic
Petr Šauer:

... impementací interface do třídy zajistím to že daná třída bude obsahovat metody přdepsané v interface ? Všechny třídy které implementují interface "IProgramator" budou mít (až je tam dopíšu) stejné metody (např. "pracuj" , "programuj"), je to tak?

 
Odpovědět 28. ledna 10:32
Avatar
Jindřich Máca
Tým ITnetwork
Avatar
 
Odpovědět 28. ledna 10:47
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 10 zpráv z 20. Zobrazit vše