Delegationsmuster
Teil des Abschnitts Objektorientierte Programmierung der Swift-Journey von Coddy. Lektion 46 von 57.
Delegation ermöglicht es einem Objekt, einige Entscheidungen über ein Protokoll an ein anderes Objekt, seinen Delegaten, zu übergeben. Das Objekt erledigt seine Aufgabe und fragt den Delegaten an den Stellen, die variieren können. Apples Frameworks verwenden dieses Muster überall:
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")Ausgabe:
50%
100%
done: photo.pngDie Delegate-Eigenschaft ist optional, und die Aufrufe verwenden delegate?., sodass das Objekt auch dann funktioniert, wenn niemand zuhört. Sie ist als weak deklariert, damit das Objekt seinen Delegate nicht selbst am Leben erhält; dadurch werden Retain-Zyklen zwischen Objekten vermieden, die aufeinander verweisen:
weak var delegate: DownloadDelegate? // erfordert, dass das Protokoll nur für Klassen gilt: AnyObject
delegate?.finished(file) // tut nichts, wenn kein Delegate vorhanden istEin Delegate kann auch Fragen beantworten, wodurch es das Verhalten des Objekts anpassen kann, ohne es zu unterklassen:
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"]))Ausgabe:
["a", "draft b"]
["a"]Delegation unterscheidet sich vom Beobachtermuster: Ein Objekt hat einen Delegate, den es fragt und informiert, während ein Subjekt viele Beobachter haben kann, die es nur informiert.
// Delegation: ein Partner, darf Fragen beantworten
weak var delegate: TableDelegate?
// Observer: beliebige Anzahl von Listenern, nur benachrichtigt
private var observers: [(Event) -> Void] = []Aufgabe
EinfachEin Checkout bittet seinen Delegaten um Entscheidungen. Das Protokoll CheckoutDelegate (nur für Klassen) besitzt discount(for total: Int) -> Int und didComplete(total: Int). Checkout besitzt ein weak var delegate, und pay(_ prices: [Int]) -> Int summiert die Preise, zieht den Rabatt des Delegaten ab (0 ohne Delegaten), teilt dem Delegaten die Endsumme mit und gibt sie zurück. Implementiere LoyaltyShop, einen Delegaten, der bei Summen von 100 oder mehr 10 % Rabatt gewährt und jede abgeschlossene Summe in receipts speichert.
Der bereitgestellte Code liest Zeilen mit durch Kommas getrennten Preisen ein. Er verarbeitet die erste Zeile ohne Delegaten und die übrigen mit einem LoyaltyShop, gibt jede Summe aus und schließlich receipts: 120 90.
Dein Code gehört in CheckoutDelegate.swift, Checkout.swift und LoyaltyShop.swift. main.swift enthält den bereitgestellten Eingabe-/Ausgabecode und darf nicht bearbeitet werden.
Probier es selbst
// Bereitgestellter Ein-/Ausgabe-Code: belasse ihn wie er ist
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: " "))
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Klassen und Objekte
Arbeiten mit DateienKlassen definierenMethoden und selfInitialisiererKlassen sind ReferenzenRückblick – Bibliotheksausweis4Kapselung
ZugriffskontrolleVon außen schreibgeschütztZustand schützenFehlschlagende InitialisiererRückblick – Bankkonto7Polymorphismus und Generika
PolymorphismusTypumwandlungGenerische FunktionenGenerische TypenZusammenfassung – Shape-Rechner2Strukturen und Werttypen
StrukturenWertsemantikVerändernde MethodenStruktur oder KlasseZusammenfassung – Inventarelement5Vererbung
UnterklassenMethoden überschreibenInitialisierer und superfinal und HierarchienRückblick – Mitarbeiterhierarchie8Standardprotokolle
CustomStringConvertibleEquatableComparableHashableRückblick – Benutzerdefiniertes Sortieren11Projekt: Bibliotheksverwaltung
Bücher und MitgliederBücher ausleihenÜbe selbstständig: Swift-Playground