Dekorierermuster
Teil des Abschnitts Objektorientierte Programmierung der Swift-Journey von Coddy. Lektion 47 von 57.
Das Dekorator-Muster fügt einem Objekt Verhalten hinzu, indem es in ein anderes Objekt mit demselben Protokoll eingeschlossen wird. Der Wrapper leitet den Aufruf an das eingeschlossene Objekt weiter und fügt seinen eigenen Teil davor oder danach hinzu:
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())Ausgabe:
espresso + milk
250Da ein Decorator selbst ein Coffee ist, können Decorators einander in beliebiger Kombination und Reihenfolge umschließen, ohne für jede Kombination eine Unterklasse zu benötigen:
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())Ausgabe:
espresso + milk + milk + syrup
330Dekoratoren eignen sich für übergreifende Ergänzungen wie Protokollierung, Caching oder Validierung rund um eine bestehende Komponente, die unverändert bleibt:
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))Ausgabe:
fetching 7
row 7Der Code, der das Objekt verwendet, sieht nur das Protokoll und kann daher ein dekoriertes Objekt nicht von einem einfachen unterscheiden. Das macht es sicher, das Muster später hinzuzufügen.
func printReceipt(_ c: any Coffee) { // funktioniert für Espresso und für jede umhüllte Version
print("\(c.label()): \(c.cost())")
}Aufgabe
EinfachPizzen werden mit Dekoratoren erstellt. Das Protokoll Pizza verfügt über price() und toppings() (ein Array mit Namen). Base kostet 800 und hat keinen Belag. Implementiere die Dekoratoren Cheese (+150), Olives (+100) und Double (verdoppelt den Preis von allem, was sich darin befindet, und wiederholt den Belag zweimal). Vervollständige build(_:), das mit Base beginnt und es in der angegebenen Reihenfolge mit einem Dekorator pro Namen umschließt.
Der bereitgestellte Code liest Zeilen mit durch Kommas getrennten Belagnamen (oder eine leere Zeile), erstellt für jede Zeile eine Pizza und gibt 1050: cheese, olives oder 800: plain aus.
Dein Code gehört in Pizza.swift, Base.swift, Cheese.swift, Olives.swift, Double.swift und Functions.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) }
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: ", ")))
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Klassen und Objekte
Arbeiten mit DateienKlassen definierenMethoden und selfInitialisiererKlassen sind ReferenzenRückblick – Bibliotheksausweis4Kapselung
ZugriffskontrolleVon außen schreibgeschütztZustand schützenFehlschlagende InitialisiererRückblick – Bankkonto7Polymorphismus und Generika
PolymorphismusTypumwandlungGenerische FunktionenGenerische TypenZusammenfassung – Shape-Rechner2Strukturen und Werttypen
StrukturenWertsemantikVerändernde MethodenStruktur oder KlasseZusammenfassung – Inventarelement5Vererbung
UnterklassenMethoden überschreibenInitialisierer und superfinal und HierarchienRückblick – Mitarbeiterhierarchie8Standardprotokolle
CustomStringConvertibleEquatableComparableHashableRückblick – Benutzerdefiniertes Sortieren11Projekt: Bibliotheksverwaltung
Bücher und MitgliederBücher ausleihenÜbe selbstständig: Swift-Playground