Wzorzec dekorator
Część sekcji Programowanie obiektowe ścieżki Swift w Coddy. Lekcja 47 z 57.
Wzorzec dekoratora dodaje zachowanie do obiektu, opakowując go w inny obiekt obsługujący ten sam protokół. Opakowujący obiekt przekazuje wywołanie do opakowanego obiektu i dodaje własną część przed nim lub po nim:
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())Wynik:
espresso + milk
250Ponieważ dekorator sam w sobie jest obiektem Coffee, dekoratory mogą opakowywać się nawzajem w dowolnej kombinacji i kolejności, bez tworzenia podklasy dla każdej kombinacji:
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())Wynik:
espresso + milk + milk + syrup
330Dekoratory sprawdzają się w przypadku dodatków przekrojowych, takich jak logowanie, buforowanie czy walidacja, otaczających istniejący komponent, który pozostaje niezmieniony:
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))Wynik:
fetching 7
row 7Kod korzystający z obiektu widzi tylko protokół, więc nie może odróżnić obiektu udekorowanego od zwykłego. Dzięki temu wzorzec można bezpiecznie dodać później.
func printReceipt(_ c: any Coffee) { // działa dla Espresso i każdej wersji opakowanej
print("\(c.label()): \(c.cost())")
}Wyzwanie
ŁatwyPizze buduje się za pomocą dekoratorów. Protokół Pizza ma metody price() i toppings() (tablicę nazw). Base kosztuje 800 i nie ma dodatków. Zaimplementuj dekoratory Cheese (+150), Olives (+100) oraz Double (podwaja cenę wszystkiego, co znajduje się wewnątrz, i powtarza dodatki dwukrotnie). Uzupełnij build(_:), która zaczyna od Base i opakowuje ją po kolei jednym dekoratorem dla każdej nazwy.
Dostarczony kod odczytuje wiersze z nazwami dodatków oddzielonymi przecinkami (lub pusty wiersz), buduje każdą pizzę i wypisuje 1050: cheese, olives lub 800: plain.
Twój kod umieść w plikach Pizza.swift, Base.swift, Cheese.swift, Olives.swift, Double.swift i Functions.swift. main.swift zawiera dostarczony kod wejścia/wyjścia i nie można go edytować.
Spróbuj swoich sił
// Dostarczony kod wejścia/wyjścia: pozostaw go bez zmian
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: ", ")))
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Klasy i obiekty
Praca z plikamiDefiniowanie klasMetody i selfInicjalizatoryKlasy są referencjamiPowtórzenie — karta biblioteczna4Hermetyzacja
Kontrola dostępuTylko do odczytu z zewnątrzOchrona stanuInicjalizatory, które mogą zawieśćPodsumowanie — konto bankowe7Polimorfizm i typy generyczne
PolimorfizmRzutowanie typówFunkcje generyczneTypy generycznePodsumowanie – kalkulator kształtów2Struktury i typy wartościowe
StrukturySemantyka wartościMetody modyfikująceStruktura czy klasaPodsumowanie – element inwentarza5Dziedziczenie
PodklasyPrzesłanianie metodInicjalizatory i superfinal i hierarchiePodsumowanie — hierarchia pracowników8Standardowe protokoły
CustomStringConvertibleEquatableComparableHashablePodsumowanie — własne sortowanie11Projekt: Zarządzanie biblioteką
Książki i czytelnicyWypożyczanie książekPoćwicz samodzielnie: Edytor online Swift