Rozszerzenia protokołów zostały wprowadzone w Swift w celu wsparcia ideologii programowania zorientowanego na protokoły: programista może wprowadzić implementacje metod domyślnych bezpośrednio na poziomie protokołu, a nie poprzez klasę bazową lub funkcje globalne. Zmniejsza to duplikację kodu i pozwala elastycznie dostosować zachowanie typów.
Problem pojawia się, gdy domyślna implementacja maskuje potrzebę własnej (przesłoniętej), lub gdy linia odpowiedzialności zostaje rozmyta — szczególnie, jeśli typ implementuje kilka protokołów z nakładaniem się. Ponadto ważne jest, aby pamiętać: jeśli metoda jest zaimplementowana w samym typie, zawsze przesłoni implementację z rozszerzenia.
Rozwiązanie — używać rozszerzeń protokołów tylko dla uniwersalnego zachowania, a w poszczególnych przypadkach jawnie implementować metodę wewnątrz typu. Należy unikać przeciążania tych samych metod w dwóch rozszerzeniach dla jednego protokołu.
Przykład kodu:
protocol Flyer { func fly() } extension Flyer { func fly() { print("Domyślne latanie") } } struct Bird: Flyer {} let sparrow = Bird() sparrow.fly() // Wyświetli: Domyślne latanie
Kluczowe cechy:
Czy rozszerzenie protokołu może dodać przechowywaną właściwość do protokołu?
Nie, tylko właściwości obliczeniowe i metody mogą być dodawane w rozszerzeniach protokołów. Właściwości przechowywane są zabronione.
Co się stanie, jeśli protokół i typ mają różne implementacje metody o tej samej nazwie? Która zostanie wywołana?
Przy bezpośrednim odniesieniu do typu zostanie wywołana implementacja typu, przy odniesieniu do instancji przez referencję typu protokołu — implementacja rozszerzenia protokołu.
protocol Greeter { func greet() } extension Greeter { func greet() { print("Cześć z rozszerzenia") } } struct Person: Greeter { func greet() { print("Cześć z typu") } } let person = Person() person.greet() // Cześć z typu let greeter: Greeter = person greeter.greet() // Cześć z rozszerzenia
Czy można w rozszerzeniu protokołu używać ograniczeń where?
Tak. To jedna z potężnych możliwości — protokół można rozszerzać tylko dla określonych typów.
extension Collection where Element: Equatable { func allEqual() -> Bool { guard let first = self.first else { return true } return allSatisfy { $0 == first } } }
** Negatywny przypadek
Zespół postanowił zaimplementować logowanie błędów przez rozszerzenie protokołów, nie przewidując, że każda usługa może chcieć dodać swój specyficzny format. W rezultacie różne usługi wywołują funkcję przez referencje do protokołu, logika zachowania różni się od oczekiwań.
Zalety:
Wady:
** Pozytywny przypadek
Protokół rozszerza się tylko dla przypadków, w których zachowanie zawsze jest uniwersalne. Dla szczególnych przypadków — jawna implementacja metod w typie, istnieje przegląd kodu w miejscach konfliktowych.
Zalety:
Wady: