Menu
Coddy logo textTech

Pattern di delega

Fa parte della sezione Programmazione orientata agli oggetti del percorso Swift di Coddy. Lezione 46 di 57.

La delega consente a un oggetto di affidare alcune decisioni a un altro oggetto, il suo delegato, tramite un protocollo. L'oggetto svolge il proprio compito e chiede al delegato nei punti che possono variare. I framework di Apple usano questo modello ovunque:

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")

Output:

50%
100%
done: photo.png

La proprietà delegato è facoltativa e le chiamate usano delegate?., quindi l'oggetto funziona anche quando nessuno è in ascolto. È dichiarata weak, così l'oggetto non mantiene da solo in vita il proprio delegato, evitando cicli di ritenzione tra oggetti che puntano l'uno all'altro:

weak var delegate: DownloadDelegate?   // richiede che il protocollo sia limitato alle classi: AnyObject
delegate?.finished(file)               // non fa nulla quando non c'è un delegate

Un delegato può anche rispondere a domande, consentendogli di personalizzare il comportamento dell'oggetto senza creare sottoclassi:

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"]))

Output:

["a", "draft b"]
["a"]

La delega differisce dal pattern observer: un oggetto ha un delegato a cui pone domande e che informa, mentre un soggetto può avere molti osservatori che informa soltanto.

// delega: un solo collaboratore, può rispondere alle domande
weak var delegate: TableDelegate?
// osservatore: un numero qualsiasi di ascoltatori, che ricevono solo notifiche
private var observers: [(Event) -> Void] = []
challenge icon

Sfida

Facile

Un Checkout chiede decisioni al suo delegato. Il protocollo CheckoutDelegate (solo classi) ha discount(for total: Int) -> Int e didComplete(total: Int). Checkout ha un weak var delegate e pay(_ prices: [Int]) -> Int somma i prezzi, sottrae lo sconto del delegato (0 in assenza di un delegato), comunica al delegato il totale finale e lo restituisce. Implementa LoyaltyShop, un delegato che applica uno sconto del 10% ai totali pari o superiori a 100 e registra ogni totale completato in receipts.

Il codice fornito legge righe di prezzi separati da virgole. Effettua il pagamento della prima riga senza un delegato e delle altre con un LoyaltyShop, stampa ogni totale e infine receipts: 120 90.

Il tuo codice va inserito in CheckoutDelegate.swift, Checkout.swift e LoyaltyShop.swift. main.swift contiene il codice di input/output fornito e non può essere modificato.

Provalo tu

// Código de entrada/salida proporcionado: mantenlo tal como está
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 iconMettiti alla prova

Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.

Tutte le lezioni di Programmazione orientata agli oggetti

Esercitati da solo: Playground Swift