Menu
Coddy logo textTech

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.png

Die 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 ist

Ein 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] = []
challenge icon

Aufgabe

Einfach

Ein 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: " "))
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung

Übe selbstständig: Swift-Playground