11. díl - Protokoly (rozhraní) ve Swift

Swift OOP Protokoly (rozhraní) ve Swift

Unicorn College ONEbit hosting Tento obsah je dostupný zdarma v rámci projektu IT lidem. Vydávání, hosting a aktualizace umožňují jeho sponzoři.

V minulé lekci, Vlastnosti ve Swift, jsme si procvičili práci s vlastnostmi. Dnes to ve Swift tutoriálu bude opět trochu teoretické, objevíme další taje objektově orientovaného programování, uvedeme si totiž tzv. rozhraní, které Swift implementuje jako protokoly.

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:

  • func utoc(souper: Bojovnik)
  • func branSe(uder: Int)
  • func nazivu() -> Bool
  • func nastavZpravu(zprava: String)
  • func vratPosledniZpravu() -> String
  • func grafickyZivot() -> String

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 např. třídu. Říkáme, že jsme vytvořili protokol. A tento protokol definující 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 Protokoly. 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, vytvoříme ptáka. Bude umět pípat, dýchat a klovat. Přidejme si třídu Ptak, bude vypadat takto:

class Ptak {

        func pipni() {
                print("♫ ♫ ♫")
        }

        func dychej() {
                print("Dýchám...")
        }

        func klovni() {
                print("Klov, klov!")
        }

}

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

let ptak = Ptak()

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

Metody na instanci ve Swift

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 protokol. Využijeme k tomu klíčového slova protocol . Názvy protokolů často končí anglickou koncovkou -able, takže můžeme mít Printable, Convertible, Renderable apod. Z protokolu je vlastně vidět, jakou funkcionalitu nabízí. Např. Renderable může znamenat, že půjde vyrenderovat, neboli zobrazit jako součást UI.

Pro zjednodušení náš protokol pojmenujeme PtakProtokol a klidně ho můžeme pro zjednodušení umístit do souboru Ptak.swift nad třídu Ptak. Také si definujeme požadované metody:

protocol PtakProtokol {

        func pipni()

        func dychej()

}

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

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

let ptak : PtakProtokol = Ptak()

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

class Ptak: PtakProtokol {
        // . . .

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

Zkusme nyní vymazat ze třídy nějakou metodu, kterou protokol udává, např. pipni(). Xcode nás upozorní, že implementace není kompletní. Vraťme ji zas zpět.

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

Metody na protokolu ve Swift

Vidíme, že na instanci můžeme nyní volat pouze metody, které poskytuje protokol. To proto, že proměnná ptak je již typu PtakProtokol, 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í protokolů dokážeme zjednodušit protokol 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 protokolu, tento kód nebude fungovat:

// tento kód nebude fungovat
let ptak : PtakProtokol = PtakProtokol()

Vícenásobná dědičnost

Swift (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 protocol, 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 protocol JesterProtokol. Bude to protokol ještěra. Ten bude umět také dýchat a ještě se plazit. Pro zjednodušení můžete protokol vytvořit v aktuálním souboru Ptak.swift nebo si založit nový Swift soubor:

protocol JesterProtokol {
        func plazSe()
        func dychej()
}

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

class PtakoJester: PtakProtokol, JesterProtokol {

}

Když nyní klikneme v tomto kódu pravým tlačítkem na PtakoJester v definici třídy, můžeme v kontextovém menu zvolit Refactor -> Add Missing Protocol Requierements a Xcode nám vygeneruje potřebné metody, takže je stačí implementovat.

Automatická implementace Swift protokolů v Xcode

Po implementaci obou protokolů vypadá kód třídy takto, <#code#> je v Xcode speciální placeholder pro kód:

class PtakoJester: PtakProtokol, JesterProtokol {

        func pipni() {
                <#code#>
        }

        func dychej() {
                <#code#>
        }

        func plazSe() {
                <#code#>
        }

        func dychej() {
                <#code#>
        }

}

Xcode z nějakého důvodu vygenerovalo metodu dychej() dvakrát, pokud se vám to také stane, stačí jednu z nich smazat.

Metody doimplementujeme:

func pipni() {
        print("♫ ♫♫ ♫ ♫ ♫♫")
}

func dychej() {
        print("Dýchám ...")
}

func plazSe() {
        print("Plazím se...")
}

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

let ptakojester = PtakoJester()

Ujistěte se, že má metody z ptáka i ještěra. Mně se bohužel zbláznilo Xcode a v nápovědě ukazovalo 3x metodu dychej(). Krátký výlet na Google ukázal, že to není ojedinělý problém... Bohužel, na druhou stranu nejde o nic kritického.

V příští lekci, Přetypování a hierarchie objektů ve Swift, budeme pokračovat v podobném duchu. Protokoly ještě neopustíme a naučíme se další pokročilé techniky objektově orientovaného programování.


 

 

Článek pro vás napsal Filip Němeček
Avatar
Jak se ti líbí článek?
Ještě nikdo nehodnotil, buď první!
Autor se příležitostně věnuje vývoji iOS aplikací či těch webových za pomocí frameworku Django. Aktuální projekt: hrejzdarma.cz Twitter: @nemecek_f (neprogramátorské tweety)
Miniatura
Předchozí článek
Vlastnosti ve Swift
Aktivity (4)

 

 

Komentáře

Avatar
Dominik Bucher:5. dubna 17:06

Jestli ještě někde uvidim programování v českym jazyce, tak se pobleju. Navíc třída protokoly implementuje, nedědí je. Nepřijde ti to trošku divný? :) To jde úplně proti tý filozofii těch protokolů... Chybí ti tu defaultní implementace protokolů přes extension a typealiasy a generics, ale pro začátečníka asi dobrý, pak bude pokáranej v práci, že řiká, že třída dědí protokol :D

Odpovědět  -4 5. dubna 17:06
Code for 6 minutes, Debug for 6 hours
Avatar
Odpovídá na Dominik Bucher
Dominik Bucher:5. dubna 17:39

Doplněk k minulýmu komentu - Co když by struct dědil protokol? :P Nepřijde ti to divný?

Odpovědět  -3 5. dubna 17:39
Code for 6 minutes, Debug for 6 hours
Avatar
David Čápka
Tým ITnetwork
Avatar
Odpovídá na Dominik Bucher
David Čápka:5. dubna 18:28

Kde je prosím tě konkrétně napsané, že protokol se dědí? Nikde v textu to nevidím. Jestli nechceš vidět programování v českém jazyce, tak pak nechoď na české stránky ;-)

Odpovědět 5. dubna 18:28
You can do anything you set your mind to.
Avatar
Filip Němeček
Redaktor
Avatar
Odpovídá na Dominik Bucher
Filip Němeček:6. dubna 9:15

Nechápu, proč tě tak pohoršuje čeština v základním tutoriálu, kde je pár řádků. Každému je snad jasné, že profesionálně se programuje anglicky.

Extensions nepovažuji za nutný základ a generics je popsané u kolekcí. Navíc každý může napsat vlastní tutoriál, nikdo ti nebrání protokoly doplnit pokročilejšími věcmi. Možnost psát články máš ve svém profilu :-)

Pokáranej v práci za "dědění protokolů"? Fakt bych chtěl vidět firmu, co bude kritizovat juniory za nedůležité slovíčkaření.. Navíc tu ani není napsané, že se protokoly dědí, ale že zápis je stejný jako, když se dědí třída.

 
Odpovědět 6. dubna 9:15
Avatar
Peter Tigranjan
Redaktor
Avatar
Peter Tigranjan:9. dubna 19:21

Ještě by to chtělo zmínit (možná v nějakém budoucím článku, ale je to o protokolech) podmíněnou implementaci, to je mocný nástroj.

 
Odpovědět 9. dubna 19:21
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 5 zpráv z 5.