Oglądając samouczek wideo dostarczony przez Apple, wydaje się, że szybka jest językiem programowania zorientowanym na protokół, a jabłko zachęca programistów do używania protokołu niż klasy. Ale z mojego osobistego poglądu nie widzę widocznych korzyści dla protokołu. klasa może być zgodna z protokołem, ale może również dziedziczyć z nadklasy. Możemy dodać rozszerzenie do protokołu, ale możemy też dodać rozszerzenie do klasy. Możemy implementować funkcje w klasach zgodnych z protokołem, ale możemy również zastąpić func w podklasie. Ciągle nie rozumiem, dlaczego potrzebujemy użyć protokołu, a nie klasy. A kiedy powinniśmy użyć protokołu zamiast klasy?Dlaczego protokół jest lepszy niż lekki?
Odpowiedz
Pozwala pobrać przykład pobierania.
was ma klasy bazowej FileDownloadModel i ma 3 podklasy AudioFileDownloadModel, VideoFileDownloadModel, ImageDownloadModel.
Masz DownloadManager że trwa wejście jako FileDownloadModel i wykorzystuje swoją urlToDownload obiekt modelu, aby pobrać plik.
Później wzdłuż linii powiedziano ci, że jest jeszcze jeden model, ale jego przyjście jak UserDownloadModel który podklasa z użytkownika.
Zobacz teraz, że trudno jest obsłużyć taki scenariusz, w którym będziesz musiał zmienić wiele kodu w celu włączenia metod pobierania.
Jak programowanie protokół zorientowany pomoże tutaj:
- Tworzenie protokół o nazwie DownloadingFileProtocol i dodać metod, które trzeba za pobranie pliku. na przykład. urlToDownload, pathToSave, rozbudowa itp
- Wdrożenie tego samego protokołu w FileDownloadModel i UserDownloadModel. Zobacz korzyść, że nie musisz zmieniać numeru kodu w UserDownloadModel. Po prostu zaimplementujesz metody z DownloadingFileProtocol.
- Zobacz, czy ponownie pojawia się nowy podmiot, nie zmienisz żadnego kodu, tylko zaimplementuj metody protokołu.
- A teraz twoja DownloadManager może wejście jako DownloadingFileProtocol zamiast konkretnego modelu, a teraz można żadnego modelu pobrać jako.
Z protokołami jedna klasa/struktura może być używana jako różne rzeczy. Na przykład, struktura String
jest zgodna z wieloma protokołami!
Comparable
CustomDebugStringConvertible
Equatable
ExtendedGraphemeClusterLiteralConvertible
Hashable
MirrorPathType
OutputStreamType
Streamable
StringInterpolationConvertible
StringLiteralConvertible
UnicodeScalarLiteralConvertible
Oznacza to, że String
może być używany jako 11 różnych rzeczy! Gdy metoda wymaga jednego z powyższych protokołów jako parametru, możesz przekazać ciąg znaków.
"Ale mogę po prostu stworzyć klasę boga, która ma wszystkie metody, które mają protokoły!" argumentowałeś. Pamiętaj, że możesz odziedziczyć tylko jedną klasę w Swift, a dziedziczenie wielu jest tak niebezpieczne, że może sprawić, że twój kod będzie bardzo złożony.
Ponadto za pomocą protokołów można zdefiniować niestandardowe zachowanie klasy. Metoda String
jest inna niż Int
. Ale oba są kompatybilne z tą funkcją:
func doStuff(x: Hashable) {
}
To jest prawdziwy cud protokołów.
Last but not least, protokoły mają sens. Protokoły reprezentują relację "jest rodzajem" lub "mogą być używane jako". Kiedy X może być użyte jako Y, ma sens, że X odpowiada protokołowi Y, prawda?
W dużej mierze jest to hierarchia typów. Powiedzmy, że masz obiekt reprezentujący GlowingRedCube
, ale chcesz mieć tego typu stosowanego w wielu kodem rodzajowy, który dba o:
- różnych kształtach -
Cube
rozciągaShape
- różnych kolorach -
Red
rozciągaColorful
- Różne rodzaje oświetlenia -
Glowing
rozciągaIlluminated
jesteś w tarapatach. Możesz wybrać klasę podstawową i dodać specjalizację: GlowingRedCube
rozszerza GlowingCube
rozszerza , ale dostajesz bardzo szeroki zestaw klas i nieelastyczny zestaw rzeczy (co jeśli chcesz zrobić SoftRedCube
, ale zachowaj wszystkie metody, które masz zdefiniowany dla twojego istniejącego typu czerwonego sześcianu?)
Mógłbyś mieć tylko Cube
i mieć właściwości podświetlenia i kształtu, ale wtedy nie otrzymujesz ładnego sprawdzania typu kompilatora: jeśli masz metodę Room.lightUp()
i musisz ją przekazać a Cube
, musisz sprawdzić, czy ten typ zawiera jakiekolwiek podświetlenie! Jeśli mógłbyś przekazać to tylko Illuminated
, kompilator zatrzymałby Cię, gdy tylko spróbujesz.
Protokoły umożliwiają oddzielenie: GlowingRedCube
może implementować protokół Illuminated
, protokół Colorful
i protokół Shape
. Z powodu rozszerzeń protokołów można uwzględnić domyślne implementacje funkcji, więc nie trzeba wybierać poziomu hierarchii, do którego ma zostać dołączony.
struct GlowingRedCube: Shape, Colorful, Illuminated {
// ..
}
Skutecznie, protokoły pozwalają dołączyć zachowanie do obiektu, niezależnie od tego, co jeszcze robi tego obiektu.Właśnie dlatego są one używane do takich funkcji, jak delegate i protokoły danych źródłowych: nawet jeśli w większości przypisujesz te rzeczy do ViewController
, podstawowy obiekt nie jest istotny, więc możesz być elastyczny w kwestii tego, jak je implementujesz.
W Swift jest dużo więcej niż tylko podstaw: są wyjątkowo wydajne, ponieważ można je dołączyć do wielu różnych konstrukcji kodu: klas, struktur i wyliczeń. To pozwala na naprawdę podejście do protokołu programowania. Jest świetny film o tym podejściu od WWDC last year, ale warto poświęcić trochę czasu na wypróbowanie różnych struktur obiektów, aby poczuć problemy.
klasa i protokół to pojęcia ortogonalne. Protokół przecina drzewo klas i dołącza do jednej lub więcej klas o odmiennym pochodzeniu.
Być może umieścić prościej:
- "klasa" definiuje obiekt jest.
- "Protokół" definiuje zachowanie obiektu.
Więc masz klasę samochodu:
class Car {
var bodyStyle : String
}
i klasy Kolor:
class Color {
var red : Int
var green : Int
var blue : Int
}
Teraz, bardziej lub mniej oczywiście kolory i samochody są całkowicie niezwiązane jednak załóżmy I chcesz mieć możliwość łatwego przekonwertowania jednego do ciągu znaków, więc mogę debugować za pomocą:
print(Car(...))
lub
print(Color(...))
Dla dokładnie tym celu język Swift definiuje protokół CustomStringConvertible
więc możemy zadeklarować samochodu można wydrukować przy użyciu tego protokołu:
extension Car : CustomStringConvertible {
var description : String { get { return "Car: \(bodyStyle)" } }
}
i kolor, a także :
extension Color : CustomStringConvertible {
var description : String { get { return "Color: \(red) \(green) \(blue)" } }
}
Więc gdzie wcześniej potrzebowałbym jednej metody drukowania dla każdej klasy, teraz potrzebuję tylko jednej metody drukowania, która wygląda na coś ike:
func print(data:CustomStringConvertible) {
let string = data.description
... bunch of code to actually print the line
}
Jest to możliwe dlatego, oświadczając, że klasa implementuje protokół jest obietnica, że mogę użyć metod z protokołem, wiedząc, że są one wdrażane i (przypuszczalnie) robić to, co się spodziewać.
ale czy nie byłeś w stanie dostosować się do wielu protokołów w ObjC? Jak Swift jest inny? – Honey
Oto mój przykład: jeśli mamy 'Car', który dziedziczy po' Vehicle' i 'Wood', który dziedziczy po' BuildingMaterial', i chcemy dodać metodę 'burn' do' Car' i 'Wood', a następnie protokół byłby najlepszy. – tktsubota
Nie jestem pewien, do którego filmu się odwołujesz, ale program WWDC 2015 [Protocol-Oriented Programming] (https://developer.apple.com/videos/play/wwdc2015/408/) opisuje, zalety protokołów. – Rob