Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.
Pouze tento týden sleva až 80 % na e-learning týkající se Java. Zároveň využij akce až 80 % zdarma při nákupu e-learningu. Více informací:

Lekce 14 - Interface (rozhraní v PHP)

V předešlém cvičení, Řešené úlohy k 12.-13. lekci OOP v PHP, jsme si procvičili nabyté zkušenosti z předchozích lekcí.

Dnes se uvedeme do rozhraní.

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, stejně jako již dříve zmíněný výčtový typ (enum). 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() : void;
}

Rozhraní Programator obsahuje jednu metodu programuj(), která nenavrací žádnou hodnotu (návratový typ void). 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 function __construct(string $jmeno, string $prijmeni, int $vek, string $heslo, public string $ide)
    {
        parent::__construct($jmeno, $prijmeni, $vek, $heslo);
    }

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

    public function programuj() : void
    {
        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');
echo("Vše OK");
class Clovek
{

    private int $unava = 0;
    public int $id;
    private static int $pocetLidi = 0;

    public function __construct(public string $jmeno, public string $prijmeni, public int $vek, private string $heslo)
    {
        self::$pocetLidi++;
        $this->id = self::$pocetLidi;
    }

    public function spi(int $doba) : void
    {
        $this->unava -= $doba * 10;
        if ($this->unava < 0)
            $this->unava = 0;
    }

    public function behej(int $vzdalenost) : void
    {
        if ($this->unava + $vzdalenost <= 20)
            $this->unava += $vzdalenost;
        else
            echo('Jsem příliš unavený.');
    }

    public function pozdrav() : void
    {
        echo('Ahoj, já jsem ' . $this->jmeno);
    }

    protected function celeJmeno() : string
    {
        return $this->jmeno . ' ' . $this->prijmeni;
    }

    public function __toString() : string
    {
        return $this->jmeno . ' - ' . $this->id;
    }

    public static function validniHeslo(string $heslo) : bool
    {
        return (mb_strlen($heslo) >= 5);
    }

}
interface Programator
{
    public function programuj() : void;
}
class Javista extends Clovek implements Programator
{

    public function __construct(string $jmeno, string $prijmeni, int $vek, string $heslo, public string $ide)
    {
        parent::__construct($jmeno, $prijmeni, $vek, $heslo);
    }

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

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

}

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:

Your page
localhost

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. Chybu bychom dostali i v případě, že bychom metodu programuj() implementovali, avšak předepsali jí jiný návratový typ, než ten předepsaný v rozhraní (void), dali jí jiný počet parametrů, nebo jim předepsali jiný datový typ. 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 />');
    }
}
class Clovek
{

    private int $unava = 0;
    public int $id;
    private static int $pocetLidi = 0;

    public function __construct(public string $jmeno, public string $prijmeni, public int $vek, private string $heslo)
    {
        self::$pocetLidi++;
        $this->id = self::$pocetLidi;
    }

    public function spi(int $doba) : void
    {
        $this->unava -= $doba * 10;
        if ($this->unava < 0)
            $this->unava = 0;
    }

    public function behej(int $vzdalenost) : void
    {
        if ($this->unava + $vzdalenost <= 20)
            $this->unava += $vzdalenost;
        else
            echo('Jsem příliš unavený.');
    }

    public function pozdrav() : void
    {
        echo('Ahoj, já jsem ' . $this->jmeno);
    }

    protected function celeJmeno() : string
    {
        return $this->jmeno . ' ' . $this->prijmeni;
    }

    public function __toString() : string
    {
        return $this->jmeno . ' - ' . $this->id;
    }

    public static function validniHeslo(string $heslo) : bool
    {
        return (mb_strlen($heslo) >= 5);
    }

}
interface Programator
{
    public function programuj() : void;
}
class Javista extends Clovek implements Programator
{

    public function __construct(string $jmeno, string $prijmeni, int $vek, string $heslo, public string $ide)
    {
        parent::__construct($jmeno, $prijmeni, $vek, $heslo);
    }

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

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

}

Aplikaci spustíme:

Your page
localhost

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é $clovek zrovna 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ř. rozhraní ProgramatorInterface.

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 Programujici.

Zdrojové kódy naleznete jako vždy ke stažení pod článkem.

V příští lekci, Rozhraní a abstraktní třída v PHP, budeme s rozhraním pokračovat a uvedeme si tzv. abstraktní třídu.


 

Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.

Stáhnout

Stažením následujícího souboru souhlasíš s licenčními podmínkami

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

 

Předchozí článek
Řešené úlohy k 12.-13. lekci OOP v PHP
Všechny články v sekci
Objektově orientované programování (OOP) v PHP
Přeskočit článek
(nedoporučujeme)
Rozhraní a abstraktní třída v PHP
Článek pro vás napsal David Čápka
Avatar
Uživatelské hodnocení:
67 hlasů
David je zakladatelem ITnetwork a programování se profesionálně věnuje 13 let. Má rád Nirvanu, nemovitosti a svobodu podnikání.
Unicorn university David se informační technologie naučil na Unicorn University - prestižní soukromé vysoké škole IT a ekonomie.
Aktivity