Menu
Coddy logo textTech

Patron de délégation

Fait partie de la section Programmation orientée objet du Journey Swift de Coddy. Leçon 46 sur 57.

La délégation permet à un objet de confier certaines décisions à un autre objet, son délégué, par l’intermédiaire d’un protocole. L’objet effectue son travail et interroge le délégué aux points qui varient. Les frameworks d’Apple utilisent ce modèle partout :

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

Sortie :

50%
100%
done: photo.png

La propriété delegate est facultative et les appels utilisent delegate?., de sorte que l’objet fonctionne même lorsque personne n’écoute. Elle est déclarée weak afin que l’objet ne maintienne pas à lui seul son delegate en vie, ce qui évite les cycles de rétention entre les objets qui pointent l’un vers l’autre :

weak var delegate: DownloadDelegate?   // nécessite que le protocole soit réservé aux classes : AnyObject
delegate?.finished(file)               // ne fait rien quand il n'y a pas de delegate

Un délégué peut également répondre à des questions, ce qui lui permet de personnaliser le comportement de l'objet sans créer de sous-classe :

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

Sortie :

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

La délégation diffère du patron observateur : un objet possède un délégué qu’il interroge et informe, tandis qu’un sujet peut avoir de nombreux observateurs qu’il informe uniquement.

// délégation : un partenaire, peut répondre aux questions
weak var delegate: TableDelegate?
// observateur : un nombre quelconque d'écouteurs, notifiés uniquement
private var observers: [(Event) -> Void] = []
challenge icon

Défi

Facile

Un Checkout demande des décisions à son délégué. Le protocole CheckoutDelegate (réservé aux classes) possède discount(for total: Int) -> Int et didComplete(total: Int). Checkout possède un weak var delegate, et pay(_ prices: [Int]) -> Int additionne les prix, soustrait la remise du délégué (0 sans délégué), informe le délégué du total final et le renvoie. Implémentez LoyaltyShop, un délégué qui accorde une remise de 10 % sur les totaux supérieurs ou égaux à 100 et enregistre chaque total finalisé dans receipts.

Le code fourni lit des lignes de prix séparés par des virgules. Il effectue le paiement de la première ligne sans délégué et celui des autres avec un LoyaltyShop, affiche chaque total et enfin receipts: 120 90.

Votre code doit se trouver dans CheckoutDelegate.swift, Checkout.swift et LoyaltyShop.swift. main.swift contient le code d’entrée/sortie fourni et ne peut pas être modifié.

Essayez vous-même

// Code d'entrée/sortie fourni : le garder tel quel
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 iconTestez-vous

Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.

Toutes les leçons de Programmation orientée objet

Entraînez-vous par vous-même : Playground Swift