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.pngLa 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 delegateUn 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] = []Sfida
FacileUn 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: " "))
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
1Classi e oggetti
Lavorare con i fileDefinire le classiMetodi e selfInizializzatoriLe classi sono riferimentiRiepilogo - Tessera della biblioteca4Incapsulamento
Controllo degli accessiSola lettura dall’esternoProteggere lo statoInizializzatori che possono fallireRiepilogo - Conto bancario7Polimorfismo e tipi generici
PolimorfismoCasting dei tipiFunzioni genericheTipi genericiRipasso - Calcolatore di forme2Struct e tipi valore
Le structSemantica dei valoriMetodi mutantiStruct o classeRiepilogo - Articolo di inventario5Ereditarietà
SottoclassiSovrascrivere i metodiInizializzatori e superfinal e gerarchieRiepilogo - Gerarchia dei dipendenti8Protocolli standard
CustomStringConvertibleEquatableComparableHashableRiepilogo - Ordinamento personalizzato11Progetto: Gestione della biblioteca
Libri e utentiPrestito dei libriEsercitati da solo: Playground Swift