Je to asi rok, kdy jsem se začal učit objektově orientovanému programování. Tehdy jsem byl v zaměstnání donucen naučit se OOP principům a to hlavně v Javě. Vzpomínám si, jaké problémy mi dělalo, než jsem pochopil význam a použití rozhraní (interface) v Javě. Tento článek je tedy zamýšlen jako taková pomůcka, která by měla objasnit používání rozhraní a to hlavně začátečníkům. Pro pochopení tohoto článku byste měli zvládat minimálně základy v Javě. Nenechte se zmást, v článku budu používat slova rozhraní i interface, které zde mají stejný význam.

Co to ten interface tedy je?

Začnu z té technické stránky věci. V Javě existují klasické třídy, abstraktní třídy a rozhraní. Zde je jejich popis:

  • Normální třída: je to ta třída, která nemá žádný z níže uvedených modifikátorů. Všechny metody, které tato třída má, mají svou implementaci. To znamená, že už obsahují kód, který něco dělá.
  • Abstraktní třída: je to třída, která má modifikátor abstract. To znamená, že chování některých metod nemusí tato třída implementovat. V tom případě může být implementace těchto metod přenechána třídám, které tuto třídu rozšiřují (dědí z ní, neboli jsou jejími potomky).
  • Rozhraní: má modifikátor interface. Rozhraní neobsahuje žádnou implementaci. Definují pouze metody, které ale neobsahují žádný kód.

(jak jsem byl upozorněn v komentáři pod článkem, abstract a interface nejsou modifikátory viz tento komentář. Až na zmatení pojmu jsou ale výše uvedené tvrzení správné :) )

Proč tedy vzniklo v Javě rozhraní? Jedním z důvodů byla skutečnost, že v Javě neexistuje vícenásobná dědičnost. Třída může mít pouze jednoho předka (to znamená, že může rozšířit pouze jednu třídu). Toto omezení má své důvody (vícenásobná dědičnost může způsobovat problémy). Použití rozhraní ve třídě označujeme jako „třída implementuje rozhraní“.

Uvedu příklad:

public interface Zobrazovac {
    void ukazZpravu(String zprava);
}

Jak vidíte, rozhraní Zobrazovac má pouze jednu metodu. Tato metoda má jeden parametr, který obsahuje textový řetězec. Podle názvu si domyslíme, co tato metoda dělá. Netušíme ale, jakým způsobem to dělá a co k tomu používá. To ani vědět nechceme. Chceme jen uživateli zobrazit zprávu, víc nás nezajímá. Nyní je třeba vytvořit nějakou třídu, která bude toto rozhraní implementovat. Pro jednoduchost vytvoříme třídu TextovyZobrazovac, která zprávu zobrazí v konzoli.

public class TextovyZobrazovac implements Zobrazovac {
   // Zde je implementace metody ukazZpravu z rozhrani Zobrazovac.
    void ukazZpravu(String zprava) {
        // Vytiskneme zpravu na konzoli.
        System.out.println(zprava);
    }
}

Teď si asi říkáte, proč to dělat tak složitě. Vždyť bychom se bez toho rozhraní obešli a ušetřili bychom si čas. Teď si ale představte, že budete chtít zprávu zobrazit nejen do konzole, ale i do souboru a v některých případech zprávu zobrazit jako formátovánou HTML stránku. Kdyby nebylo vytvořeno rozhraní Zobrazovac, museli byste vytvořit několik tříd, které by se staraly o různé způsoby zobrazení zprávy. Při jejich použití v programu byste ale museli vědět, co to jsou za třídy, jaké metody pro zobrazení zprávy používají a případně znát další detaily.

V našem případě jsme si ale zavedli rozhraní Zobrazovac a tudíž můžeme ve svém kódu vytvořit třeba pole typu Zobrazovac, do kterého budeme ukládat jednotlivé zobrazovače, které mají být aktivní. V případě potřeby potom projedeme všechny prvky tohoto pole a zavoláme nad nimi metodu ukazZpravu(). Víme totiž, že všechny instance (různých) tříd v poli implementují Zobrazovac, tudíž tuto metodu mají. Nemusíme se tedy starat o to, jaké třídy vlastně máme, víme jen, že umí zobrazit nějakým způsobem zprávu.

Příklad ze života

Pokusím se princip rozhraní vysvětlit na nějakém příkladu ze života. Vemte si třeba počítačovou myš. Žádnou konkrétní, jen si představte zjednodušený model myši. Představte si myš jako rozhraní, které nám poskytuje kliknutí levým tlačítkem a kliknutí pravým tlačítkem. Dejme tomu, že toto je základní rozhraní pro všechny myši. Když se naučíte toto rozhraní, budete umět ovládat všechny myši, které mají toto rozhraní. Bude vám jedno, jestli bude myš plastová, nebo kovová. Optická nebo kuličková. Modrá nebo bílá. Kulatá nebo hranatá. Vám stačí jen vědět, že s ní můžete provést klik levým, nebo pravým tlačítkem.

Tyto dvě tlačítka jsou tedy rozhraní myši a vám je upřímně jedno, co se při stisku tlačítka děje. Neřešíte, jak to uvnitř myši funguje – jestli jsou tam nějaké mechanické části, nějaké optické čidla, nebo skřítci, kteří sledují pohyb tlačítka. Stejně je to i s rozhraním v Javě. Na úrovni rozhraní nevidíte dovnitř – nevíte jak se daná úloha provede.

Teď si představte, že se vám do ruky dostane nějaká herní myš, která bude krom hlavních dvou tlačítek obsahovat desítky dalších tlačítek a koleček. I přesto ale budete tuto myš schopni ovládat, protože pořád implementuje rozhraní klasické myši – má přeci levé a pravé tlačítko. Zbytek vás v danou chvíli nezajímá. Stejně i v našem příkladě nás nezajímá, co všechno třída TextovyZobrazovac všechno umí. Stačí jen vědět, že implementuje rozhraní Zobrazovac, díky kterému ji můžeme používat pro naše účely.

Ukažme si tedy příklad s myšmi v Java kódu:

/**
 * Rozhraní klasické myši. Jen dvě tlačítka, které lze stisknout.
 */
public interface KlasickaMys {
    void kliknutiLevymTlacitkem();
    void kliknutiPravymTlacitkem();
}
 
/**
 * Tato myš je úplně obyčejná myš, která nemá žádné funkce navíc.
 */
public class UplneObycejnaMys implements KlasickaMys {
    void kliknutiLevymTlacitkem() {
        // Zde bude napsaný kód, který se provede při stisku levého tlačítka.
        // Tato metoda je implementací metody z rozhraní KlasickaMys
    }
    void kliknutiPravymTlacitkem() {
        // Zde bude napsaný kód, který se provede při stisku pravého tlačítka.
        // Tato metoda je implementací metody z rozhraní KlasickaMys
    }
}
 
/**
 * Tato myš má v sobě zabudovanou lampičku.
 */
public class MysSLampickou implements KlasickaMys {
 
    void kliknutiLevymTlacitkem() {
        // Zde bude napsaný kód, který se provede při stisku levého tlačítka.
        // Tato metoda je implementací metody z rozhraní KlasickaMys
    }
    void kliknutiPravymTlacitkem() {
        // Zde bude napsaný kód, který se provede při stisku pravého tlačítka.
        // Tato metoda je implementací metody z rozhraní KlasickaMys
    }
    void rozsvitLampicku() {
        // Jak vidíte, toto je funkce navíc, kterou klasické myši nemají.
    }
}

Tak a teď zkusíme tyto myši použít. Vytvoříme třídu Uzivatel, která bude obsahovat pole s objekty implementujícími interface KlasickaMys. Nejdříve toto pole naplníme nějakými objekty a následně projedeme všechny prvky pole a otestujeme na nich klepnutí na levé a pravé tlačítko.

public class Uzivatel {
 
     private KlasickaMys[] poleMysi;
 
      /**
      * V konstruktoru vytvoříme instance několika myší, které má uživatel
      * k dispozici, aby je poté mohl vyzkoušet.
      */
     public Uzivatel() {
         // Vytvoření pole, do kterého umístíme dvě různé myši, které budou implementovat interface KlasickaMys
         poleMysi = new KlasickaMys[2];
 
         // Přidáme do pole s myšmi obyčejnou myš.
         KlasickaMys mys1 = new UplneObycejnaMys();
         poleMysi[0] = mys1;
 
         // Přidáme do pole s myšmi myš s lampičkou.
         KlasickaMys mys2 = new MysSLampickou();
         poleMysi[1] = mys2;
     }
 
     /**
      * Této metodě předáme v parametru jakýkoliv objekt, který implementuje interface KlasickaMys.
      * Díky tomu víme, že můžeme otestovat stisk levého a pravého tlačítka. To co se po kliknutí
      * stane už záleží na konkrétní implementaci. U myši s lampičkou zde ale nemůžeme otestovat
      * funkci rozsvitLampicku(), protože není obsažená v rozhraní KlasickaMys.
      */
     public void otestujMys(KlasickaMys mysNaOtestovani) {
         mysNaOtestovani.kliknutiLevymTlacitkem();
         mysNaOtestovani.kliknutiPravymTlacitkem();
     }
 
     /**
      * V této metodě si uživatel zkusí u každé myši uložené v poli poleMysi
      * kliknutí levým i pravým tlačítkem.
      */
    public void vyberMys() {
        for (int i = 0; i < poleMysi.lenght; i++) {
            otestujMys(poleMysi[i]);
        }
    }
 
}

Když pak vymyslíme nějakou novou, lepší myš, jednoduše vytvoříme instanci nové myši a vložíme ji do pole s myšmi. Nemusíme už tedy měnit žádné další části programu, protože každá myš, která implementuje rozhraní KlasickaMys bude umět pořád ten stejný základ – kliknutí levým a pravým tlačítkem.

Tak jako ve skutečném světě, i ve svém programu byste měli skrývat detaily o implementaci. U myši taky určitě nechcete vědět, co všechno se děje při stisku tlačítka. Nechcete zkoumat při koupi nové myši, jak se ovládá. Chcete nějaké jednotné rozhraní, které bude společné pro všechny myši. I u auta vás bude zajímat především jeho rozhraní. Když sednete do nového auta a bude mít stejné rozhraní (volant, pedály…), budete ho umět ovládat. Teď si ale představte, že byste sedli do nového auta a tam by místo volantu a pedálu byl umístěn knipl jako v letadle. Toto auto by neimplementovalo rozhraní klasického auta a proto byste ho museli používat zcela jinak než klasické auta.

Shrnutí

Jak vidíte, používání rozhraní přináší mnohé výhody. I když to zpočátku vypadá, že používat rozhraní je zbytečná práce navíc, není to tak. Pokud se budete snažit používat rozhraní co nejvíce, uvidíte, že si ušetříte spoustu času. Jakmile zjistíte na vlastní kůži ty výhody, určitě začnete rozhraní používat pravidelně.

Pokud vám přiložené příklady nestačily, na internetu najdete určitě spoustu dalších příkladu. Další možná ukázka rozhraní je při použití Observeru (Observer, Listener…to jsou slova které si zkuste vyhledat). Po přečtení tohoto článku si zkuste vymyslet nějaké další příklady, kde by bylo vhodné použít rozhraní. Zkuste si tyto příklady převést do kódu a naprogramovat to.

Pokoušel jsem se článek napsat co nejjednodušeji. Pokud se vám ale bude zdát něco nesrozumitelné, budu velmi rád, když mě na to upozorníte v komentáři pod článkem.

Další články zde na webu: