Wzorzec strategii
Część sekcji Programowanie obiektowe ścieżki Swift w Coddy. Lekcja 45 z 57.
Wzorzec strategii umieszcza każdy wariant algorytmu we własnym typie, który implementuje wspólny protokół. Kod, który z niego korzysta, przechowuje strategię i może ją zmienić w czasie działania programu:
protocol ShippingStrategy {
func cost(weight: Int) -> Int
}
struct Standard: ShippingStrategy { func cost(weight: Int) -> Int { 5 + weight } }
struct Express: ShippingStrategy { func cost(weight: Int) -> Int { 15 + weight * 2 } }
struct Checkout {
var shipping: any ShippingStrategy
func total(price: Int, weight: Int) -> Int { price + shipping.cost(weight: weight) }
}
var c = Checkout(shipping: Standard())
print(c.total(price: 100, weight: 3))
c.shipping = Express()
print(c.total(price: 100, weight: 3))Wynik:
108
121W Swift do strategii z jedną metodą często wystarczy domknięcie. Typ przechowuje funkcję i ją wywołuje:
struct Sorter {
var rule: (String, String) -> Bool
func sort(_ words: [String]) -> [String] { words.sorted(by: rule) }
}
let words = ["banana", "kiwi", "apple"]
var s = Sorter(rule: <)
print(s.sort(words))
s.rule = { $0.count < $1.count }
print(s.sort(words))Wynik:
["apple", "banana", "kiwi"]
["kiwi", "apple", "banana"]Strategia zastępuje switch wewnątrz algorytmu. Nowe warianty to nowe typy (lub domknięcia), a kod, który z nich korzysta, pozostaje niezmieniony:
// bez strategii: każda nowa opcja wymaga zmiany tej funkcji
func cost(kind: String, weight: Int) -> Int {
switch kind { case "standard": return 5 + weight; default: return 15 + weight * 2 }
}
// ze strategią: Checkout nigdy się nie zmienia
struct Overnight: ShippingStrategy { func cost(weight: Int) -> Int { 40 } }Strategie można wybierać na podstawie danych, na przykład ustawienia użytkownika, za pomocą prostej fabryki:
protocol Discount { func apply(_ price: Int) -> Int }
struct NoDiscount: Discount { func apply(_ p: Int) -> Int { p } }
struct Percent: Discount { let rate: Int; func apply(_ p: Int) -> Int { p - p * rate / 100 } }
func discount(for member: String) -> any Discount {
member == "gold" ? Percent(rate: 20) : NoDiscount()
}
print(discount(for: "gold").apply(200))
print(discount(for: "basic").apply(200))Wynik:
160
200Wyzwanie
ŁatwyAtak postaci w grze to strategia. Protokół Attack ma name i damage(strength:) -> Int. Zaimplementuj Sword (strength * 2), Bow (strength + 5) i Magic (przechowującą mana; zadającą obrażenia równe strength * 3, gdy mana wynosi co najmniej 10, a w przeciwnym razie 1; samo damage nie zmienia many). Hero przechowuje strength i attack, a strike() zwraca Ada hits with sword for 20.
Udostępniony kod odczytuje imię i siłę bohatera, a następnie polecenia equip sword, equip bow, equip magic 15 lub strike. Wypisuje wynik każdego ataku.
Twój kod umieść w plikach Attack.swift, Sword.swift, Bow.swift, Magic.swift i Hero.swift. main.swift zawiera udostępniony 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) }
let head = input[0].split(separator: ",").map(String.init)
var hero = Hero(name: head[0], strength: Int(head[1])!, attack: Sword())
for cmd in input.dropFirst() {
let p = cmd.split(separator: " ").map(String.init)
if p[0] == "strike" { print(hero.strike()); continue }
switch p[1] {
case "bow": hero.attack = Bow()
case "magic": hero.attack = Magic(mana: Int(p[2])!)
default: hero.attack = Sword()
}
}
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