14. díl - Rozhraní (interface)

C# .NET Objektově orientované programování Rozhraní (interface) American English version English version

V minulém dílu seriálu tutoriálů o C# jsme si procvičili práci s kolekcí List při tvorbě elektronického diáře. Dnes to bude opět trochu teoretické, objevíme další taje objektově orientovaného programování, uvedeme si totiž rozhraní.

Rozhraní

Rozhraním objektu se myslí to, jak je objekt viditelný zvenku. Již víme, že objekt obsahuje nějaké metody, ty mohou být privátní nebo veřejné. Rozhraní objektu tvoří právě jeho veřejné metody, je to způsob, jakým s určitým typem objektu můžeme komunikovat. Již jsme několikrát mohli vidět jaké veřejné metody naše třída nabízí, např. u našeho bojovníka do arény. Třída Bojovnik měla následující veřejné metody:

  • void Utoc(Bojovnik souper)
  • void BranSe(int uder)
  • bool Nazivu()
  • void NastavZpravu(string zprava)
  • string VratPosledniZ­pravu()
  • string GrafickyZivot()

Pokud si do nějaké úproměnné uložíme instanci bojovníka, můžeme na ni volat metody jako Utoc() nebo BranSe(). To pořád není nic nového, že?

My si však rozhraní můžeme deklarovat zvlášť a to podobným způsobem jako třeba třídu. Toto rozhraní poté použijeme jako datový typ.

Vše si vyzkoušíme, ale na něčem jednodušším, než je bojovník. Vytvořme si nový projekt, konzolovou aplikaci a nazvěme ho Rozhrani. Přidáme si nějakou jednoduchou třídu. Protože by se dle mého názoru měla teorie vysvětlovat na něčem odlehčujícím, uděláme si Ptáka. Bude umět pípat, dýchat a klovat. Přidejme si třídu Ptak, bude vypadat takto:

class Ptak
{

        public void Pipni()
        {
                Console.WriteLine("♫ ♫ ♫");
        }

        public void Dychej()
        {
                Console.WriteLine("Dýchám...");
        }

        public void Klovni()
        {
                Console.WriteLine("Klov, klov!");
        }

}

Třída je opravdu triviální. Přejděme do program.cs a vytvořme si instanci ptáka:

Ptak ptak = new Ptak();

Nyní napíšeme ptak. a necháme Visual Studio, aby nám zobrazilo metody na třídě (lze také vyvolat stiskem ctrl + space):

Metody ptáka

Vidíme, co na ptákovi můžeme vše volat, jsou tam samozřejmě ty 3 metody, co jsme ve třídě implementovali (plus další, které mají objekty v základu).

Nyní ptákovi vytvoříme rozhraní. Využijeme k tomu klíčového slova interface (anglicky rozhraní). Je dobrým zvykem, že rozhraní začíná vždy na písmeno I (jako Interface). Vytvořme tedy interface IPtak. Pravým tlačítkem klikneme na projekt, a přidáme nový item -> Interface.

Nový interface ve Visual Studiu

K projektu se nám přidá prázdný interface. Do něj přidáme hlavičky metod, které má dané rozhraní obsahovat. Samotnou implementaci (kód metod) uvedeme až ve třídě, která bude toto rozhraní implementovat (viz dále).

Do rozhraní IPtak tedy přidáme hlavičky metod, schválně jednu vynecháme a přidáme pouze Pípání a Dýchání:

interface IPtak
{
        void Pipni();
        void Dychej();
}

Modifikátor public neuvádíme, protože rozhraní obsahuje vždy pouze veřejné metody (jinak by nemělo smysl, udává, jak s objektem zvenku pracovat).

Vraťme se do program.cs a změňme řádek s proměnnou ptak tak, aby již nebyla typu Ptak, ale IPtak:

IPtak ptak = new Ptak();

Kódem výše říkáme, že v proměnné typu IPtak očekáváme objekt, který obsahuje ty metody, co jsou v rozhraní. Visual Studio nám vyhubuje, protože třída Ptak zatím rozhraní IPtak neobsahuje, i když potřebné metody má, neví, že rozhraní poskytuje. Přesuneme se do třídy Ptak a nastavíme jí, že implementuje interface IPtak. Dělá se to stejně, jako když třída od jiné dědí:

class Ptak: IPtak
{
 . . .

Když se nyní vrátíme do program.cs, řádek s proměnnou typu IPtak je již v pořádku, třída Ptak korektně implementuje rozhraní IPtak a její instance může být do proměnné tohoto typu uložena.

Zkusme nyní vymazat ze třídy nějakou metodu, kterou rozhraní udává, třeba Pipni(). Visual Studio nás upozorní, že implementace není kompletní. Vraťme ji zas zpět.

Opět přidáme řádek ptak., Visual Studio nám nabídne následující metody:

Metody ptáka s rozhraním IPtak

Vidíme, že na instanci můžeme nyní volat pouze metody, které poskytuje rozhraní. To proto, že proměnná ptak je již typu IPtak, nikoli Ptak. Metoda Klovni() úplně chybí.

K čemu je to dobré? Výhod a využití je více, na první jsme již přišli. Pomocí rozhraní dokážeme zjednodušit rozhraní nějakého složitého objektu a vystavit jen tu část, která se nám v tu dobu hodí.

Ještě dodám, že nemůžeme vytvořit instanci z rozhraní, tento kód nebude fungovat:

// tento kód nebude fungovat
IPtak ptak = new IPtak();

Vícenásobná dědičnost

C# (stejně jako většina programovacích jazyků) nepodporuje vícenásobnou dědičnost. Nemůžeme tedy jednu třídu oddědit z několika jiných tříd. Je to hlavně z toho důvodu, že může vyvstat problém kolize názvů metod v různých třídách, ze kterých dědíme. Vícenásobná dědičnost se často obchází právě přes interface, protože těch můžeme ve třídě implementovat kolik chceme. Umožňuje nám to s instancí poté pracovat určitým způsobem a vůbec nás nezajímá, jakého typu objekt ve skutečnosti je a co všechno navíc obsahuje.

Přidejme si k projektu interface IJester. Bude to interface ještěra. Ten bude umět také dýchat a ještě se plazit:

interface IJester
{
        void PlazSe();
        void Dychej();
}

Vyzkoušejme si "vícenásobnou dědičnost", přesněji implementaci více rozhraní v jedné třídě. Udělejme si ptakoještěra. Přidejme k projektu třídu PtakoJester.cs. Bude implementovat rozhraní IPtak a IJester:

class PtakoJester: IJester, IPtak
{
}

Když nyní klikneme v tomto kódu pravým tlačítkem na jednotlivé interface (tedy na IJester a IPtak), můžeme v kontextovém menu zvolit možnost Implement Interface. Visual Studio nám poté do třídy automaticky vygeneruje potřebné metody.

Automatická implementace

Po implementaci obou rozhraní vypadá kód třídy takto:

class PtakoJester: IJester, IPtak
{
        public void PlazSe()
        {
                throw new NotImplementedException();
        }

        public void Dychej()
        {
                throw new NotImplementedException();
        }

        public void Pipni()
        {
                throw new NotImplementedException();
        }
}

Metody doimplementujeme:

public void PlazSe()
{
        Console.WriteLine("Plazím se...");
}

public void Dychej()
{
        Console.WriteLine("Dýchám...");
}

public void Pipni()
{
        Console.WriteLine("♫ ♫♫ ♫ ♫ ♫♫");
}

A přesuňme se do program.cs a vytvořme si instanci ptakoještěra:

PtakoJester ptakojester = new PtakoJester();

Ujistěme se, že má metody jak ptáka, tak ještěra:

Metody ptáka a ještěra

Příště budeme pokračovat v podobném duchu, rozhraní ještě neopustíme a naučíme se další pokročilé techniky objektově orientovaného programování.


 

Stáhnout

Staženo 652x (23.49 kB)
Aplikace je včetně zdrojových kódů v jazyce C#

 

  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 (12 hlasů) :
4.754.754.754.754.75


 



 

 

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

Avatar
hanpari
Redaktor
Avatar
hanpari:

Nejspis proto, ze c.# nepodporuje vicenasobnou dedicnost.

 
Odpovědět 15.2.2015 7:34
Avatar
NeonMaster
Člen
Avatar
Odpovídá na hanpari
NeonMaster:

Aha... ale výše je napsane že se to da obejit pres interface pritom neni zadny rozdil mezitim jestli tam ty interface vytvořim nebo ne..

 
Odpovědět 15.2.2015 10:10
Avatar
hanpari
Redaktor
Avatar
Odpovídá na NeonMaster
hanpari:

Právě teď to asi nemá velký smysl. Ale dejme tomu, že v budoucnu budeš mít nějaký objekt a jediné, co budeš potřebovat, je mít jistotu, že nějakým způsobem dejme tomu implementuje rozhraní IPtak, protože budeš potřebovat, aby ten objekt pípnul. A nebude tě zajímat, zda je to pták nebo polopták.

OOP je o tom, že se snažíš přemýšlet hodně hodně dopředu :)

 
Odpovědět  ±0 15.2.2015 14:11
Avatar
Jiří Janeček (Johnny):

Na komentář "...jelokoz dvakrát implementuje stejne metody.." od miroslav.siska si reagoval, že "..tu metodu máš implementovanou jen jednou..". Přišlo mi to trochu zmatené, a tak jsem si ten kód prošel ještě jednou a jestli to tedy chápu, v tom původním příkladě (třída Ptak a rozhraní IPtak) došlo k implementaci metody Dychej() u IPtak tímto kódem

IPtak ptak = new Ptak();

Dá se to tak říct?

 
Odpovědět 29.9.2015 17:13
Avatar
Jiří Janeček (Johnny):

hanpari: Omlouvám se, zase nevratitelný dislike..

 
Odpovědět 20.10.2015 20:46
Avatar
Pavel Štumpf:

Super článek, děkuji.

 
Odpovědět 7.11.2015 10:56
Avatar
Pavel Štumpf:

Chtěl bych se zeptat, jestli je nutné v programu explicitně definovat daný interface. Studuji teď jeden program a tam se používá dědění z interface, ale takto explicitně definované interface:

interface IJester
{
        void PlazSe();
        void Dychej();
}

naprosto chybí, ale program přesto nehlásí chybu. Je možné, že je definováno v jiné části prgramu? Zdroják bohužel nemohu sdílet. Díky za případnou odpověď.

 
Odpovědět 7.11.2015 11:25
Avatar
Odpovídá na Pavel Štumpf
Pavel Štumpf:

Ok, tak jsem právě objevil metadata, kde je interface definováno :-)

 
Odpovědět 7.11.2015 12:08
Avatar
Štefan Pružinský:

Ďakujem za výborný článok. :)

Odpovědět 13. února 16:50
Najefektívnejším spôsobom debuggingu je modlitba. :)
Avatar
Honza.G
Člen
Avatar
Honza.G:

Zdravím :) chtěl bych se zeptat, ty metody mám doiplementovat tak že je dopíšu klasicky: " console....." Nebo je nato nějakej fígl? po importaci tam mám to: "throw new" a ještě v nabídce mi to nenabízí všechny ty metody. Předem dík moc :)

 
Odpovědět 24. července 9:30
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 48. Zobrazit vše