Patron décorateur
Fait partie de la section Programmation orientée objet du Journey Swift de Coddy. Leçon 47 sur 57.
Le modèle décorateur ajoute un comportement à un objet en l’enveloppant dans un autre objet utilisant le même protocole. L’enveloppe transmet l’appel à l’objet enveloppé et ajoute sa propre partie avant ou après :
protocol Coffee {
func cost() -> Int
func label() -> String
}
struct Espresso: Coffee {
func cost() -> Int { 200 }
func label() -> String { "espresso" }
}
struct WithMilk: Coffee {
let base: any Coffee
func cost() -> Int { base.cost() + 50 }
func label() -> String { base.label() + " + milk" }
}
let drink = WithMilk(base: Espresso())
print(drink.label())
print(drink.cost())Sortie :
espresso + milk
250Comme un décorateur est lui-même un Coffee, les décorateurs peuvent s’imbriquer les uns dans les autres dans n’importe quelle combinaison et dans n’importe quel ordre, sans avoir besoin d’une sous-classe pour chaque combinaison :
protocol Coffee { func cost() -> Int; func label() -> String }
struct Espresso: Coffee { func cost() -> Int { 200 }; func label() -> String { "espresso" } }
struct WithMilk: Coffee {
let base: any Coffee
func cost() -> Int { base.cost() + 50 }
func label() -> String { base.label() + " + milk" }
}
struct WithSyrup: Coffee {
let base: any Coffee
func cost() -> Int { base.cost() + 30 }
func label() -> String { base.label() + " + syrup" }
}
let fancy = WithSyrup(base: WithMilk(base: WithMilk(base: Espresso())))
print(fancy.label())
print(fancy.cost())Sortie :
espresso + milk + milk + syrup
330Les décorateurs conviennent aux ajouts transversaux tels que la journalisation, la mise en cache ou la validation autour d’un composant existant, qui reste inchangé :
protocol DataSource { func fetch(_ id: Int) -> String }
struct Database: DataSource { func fetch(_ id: Int) -> String { "row \(id)" } }
struct Logged: DataSource {
let inner: any DataSource
func fetch(_ id: Int) -> String {
print("fetching \(id)")
return inner.fetch(id)
}
}
let source: any DataSource = Logged(inner: Database())
print(source.fetch(7))Sortie :
fetching 7
row 7Le code qui utilise l’objet ne voit que le protocole, il ne peut donc pas distinguer un objet décoré d’un objet ordinaire. C’est ce qui rend le modèle sûr à ajouter ultérieurement.
func printReceipt(_ c: any Coffee) { // fonctionne pour Espresso et pour toute version encapsulée
print("\(c.label()): \(c.cost())")
}Défi
FacileLes pizzas sont construites à l’aide de décorateurs. Le protocole Pizza possède price() et toppings() (un tableau de noms). Base coûte 800 et ne comporte aucune garniture. Implémentez les décorateurs Cheese (+150), Olives (+100) et Double (double le prix de tout ce qui se trouve à l’intérieur et répète deux fois ses garnitures). Complétez build(_:), qui commence par Base et l’enveloppe avec un décorateur par nom, dans l’ordre.
Le code fourni lit des lignes contenant des noms de garnitures séparés par des virgules (ou une ligne vide), construit chaque pizza et affiche 1050: cheese, olives ou 800: plain.
Votre code doit se trouver dans Pizza.swift, Base.swift, Cheese.swift, Olives.swift, Double.swift et Functions.swift. main.swift contient le code fourni pour l’entrée et la sortie 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) }
for line in input {
let names = line.isEmpty ? [] : line.split(separator: ",").map(String.init)
let p = build(names)
let tops = p.toppings()
print("\(p.price()): " + (tops.isEmpty ? "plain" : tops.joined(separator: ", ")))
}
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
1Classes et objets
Travailler avec des fichiersDéfinir des classesMéthodes et selfInitialiseursLes classes sont des référencesRécapitulatif - Carte de bibliothèque4Encapsulation
Contrôle d’accèsLecture seule depuis l’extérieurProtection de l’étatInitialiseurs pouvant échouerRécapitulatif - Compte bancaire7Polymorphisme et génériques
PolymorphismeConversion de typeFonctions génériquesTypes génériquesRécapitulatif - Calculateur de formes2Structures et types valeur
StructuresSémantique de valeurMéthodes mutantesStructure ou classeRécapitulatif - article d’inventaire5Héritage
Sous-classesRedéfinition des méthodesInitialiseurs et superfinal et hiérarchiesRécapitulatif - hiérarchie des employés8Protocoles standard
CustomStringConvertibleEquatableComparableHashableRécapitulatif - Tri personnalisé11Projet : Gestion de bibliothèque
Livres et membresEmprunter des livresEntraînez-vous par vous-même : Playground Swift