Wzorzec delegowania
Część sekcji Programowanie obiektowe ścieżki Swift w Coddy. Lekcja 46 z 57.
Delegowanie pozwala obiektowi przekazać część decyzji innemu obiektowi, swojemu delegatowi, za pośrednictwem protokołu. Obiekt wykonuje swoje zadanie i pyta delegata w tych momentach, które mogą się różnić. Frameworki Apple używają tego wzorca wszędzie:
protocol DownloadDelegate: AnyObject {
func progress(_ percent: Int)
func finished(_ file: String)
}
final class Downloader {
weak var delegate: DownloadDelegate?
func download(_ file: String) {
for p in [50, 100] { delegate?.progress(p) }
delegate?.finished(file)
}
}
final class Screen: DownloadDelegate {
func progress(_ percent: Int) { print("\(percent)%") }
func finished(_ file: String) { print("done: \(file)") }
}
let screen = Screen()
let d = Downloader()
d.delegate = screen
d.download("photo.png")Wynik:
50%
100%
done: photo.pngWłaściwość delegata jest opcjonalna, a wywołania używają delegate?., więc obiekt działa nawet wtedy, gdy nikt nie nasłuchuje. Jest zadeklarowana jako weak, dzięki czemu obiekt sam nie utrzymuje swojego delegata przy życiu, co zapobiega cyklom silnych referencji między obiektami wskazującymi na siebie nawzajem:
weak var delegate: DownloadDelegate? // wymaga, aby protokół był ograniczony do klas: AnyObject
delegate?.finished(file) // nie robi nic, gdy nie ma delegateDelegat może również odpowiadać na pytania, co pozwala mu dostosowywać zachowanie obiektu bez tworzenia podklasy:
protocol ListDelegate: AnyObject {
func shouldShow(_ item: String) -> Bool
}
final class ListView {
weak var delegate: ListDelegate?
func render(_ items: [String]) -> [String] {
items.filter { delegate?.shouldShow($0) ?? true }
}
}
final class HideDrafts: ListDelegate {
func shouldShow(_ item: String) -> Bool { !item.hasPrefix("draft") }
}
let view = ListView()
let rules = HideDrafts()
print(view.render(["a", "draft b"]))
view.delegate = rules
print(view.render(["a", "draft b"]))Wynik:
["a", "draft b"]
["a"]Delegowanie różni się od wzorca obserwatora: obiekt ma jednego delegata, którego pyta i informuje, podczas gdy podmiot może mieć wielu obserwatorów, których tylko informuje.
// delegowanie: jeden partner, może odpowiadać na pytania
weak var delegate: TableDelegate?
// obserwator: dowolna liczba odbiorców, którzy są jedynie powiadamiani
private var observers: [(Event) -> Void] = []Wyzwanie
ŁatwyCheckout prosi swojego delegata o podjęcie decyzji. Protokół CheckoutDelegate (tylko dla klas) zawiera discount(for total: Int) -> Int oraz didComplete(total: Int). Checkout ma weak var delegate, a metoda pay(_ prices: [Int]) -> Int sumuje ceny, odejmuje zniżkę delegata (0, jeśli nie ma delegata), informuje delegata o końcowej sumie i ją zwraca. Zaimplementuj LoyaltyShop — delegata, który udziela 10% zniżki przy sumach wynoszących co najmniej 100 i zapisuje każdą ukończoną transakcję w receipts.
Dostarczony kod odczytuje wiersze z cenami oddzielonymi przecinkami. Opłaca pierwszy wiersz bez delegata, a pozostałe z użyciem LoyaltyShop, wyświetla każdą sumę, a na końcu receipts: 120 90.
Twój kod umieść w plikach CheckoutDelegate.swift, Checkout.swift i LoyaltyShop.swift. main.swift zawiera dostarczony kod wejścia/wyjścia i nie można go edytować.
Spróbuj swoich sił
// Dostarczony kod wejścia/wyjścia: pozostaw go bez zmian
var input: [String] = []
while let line = readLine() { input.append(line) }
let checkout = Checkout()
let shop = LoyaltyShop()
for (i, line) in input.enumerated() {
if i == 1 { checkout.delegate = shop }
print(checkout.pay(line.split(separator: ",").map { Int($0)! }))
}
print("receipts: " + shop.receipts.map { String($0) }.joined(separator: " "))
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Klasy i obiekty
Praca z plikamiDefiniowanie klasMetody i selfInicjalizatoryKlasy są referencjamiPowtórzenie — karta biblioteczna4Hermetyzacja
Kontrola dostępuTylko do odczytu z zewnątrzOchrona stanuInicjalizatory, które mogą zawieśćPodsumowanie — konto bankowe7Polimorfizm i typy generyczne
PolimorfizmRzutowanie typówFunkcje generyczneTypy generycznePodsumowanie – kalkulator kształtów2Struktury i typy wartościowe
StrukturySemantyka wartościMetody modyfikująceStruktura czy klasaPodsumowanie – element inwentarza5Dziedziczenie
PodklasyPrzesłanianie metodInicjalizatory i superfinal i hierarchiePodsumowanie — hierarchia pracowników8Standardowe protokoły
CustomStringConvertibleEquatableComparableHashablePodsumowanie — własne sortowanie11Projekt: Zarządzanie biblioteką
Książki i czytelnicyWypożyczanie książekPoćwicz samodzielnie: Edytor online Swift