Pattern Strategy
Fa parte della sezione Programmazione orientata agli oggetti del percorso Swift di Coddy. Lezione 45 di 57.
Il pattern strategy inserisce ogni variante di un algoritmo nel proprio tipo, dietro un protocollo condiviso. Il codice che lo usa conserva una strategia e può sostituirla durante l’esecuzione:
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))Output:
108
121In Swift, una closure è spesso sufficiente per una strategia con un solo metodo. Il tipo memorizza una funzione e la chiama:
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))Output:
["apple", "banana", "kiwi"]
["kiwi", "apple", "banana"]La strategia sostituisce uno switch all'interno dell'algoritmo. Le nuove varianti sono nuovi tipi (o chiusure) e il codice che le usa rimane invariato:
// senza una strategia: ogni nuova opzione richiede di modificare questa funzione
func cost(kind: String, weight: Int) -> Int {
switch kind { case "standard": return 5 + weight; default: return 15 + weight * 2 }
}
// con una strategia: Checkout non cambia mai
struct Overnight: ShippingStrategy { func cost(weight: Int) -> Int { 40 } }Le strategie possono essere scelte in base ai dati, ad esempio da un'impostazione dell'utente, con una piccola factory:
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))Output:
160
200Sfida
FacileL'attacco di un personaggio di un gioco è una strategia. Il protocollo Attack ha name e damage(strength:) -> Int. Implementa Sword (strength * 2), Bow (strength + 5) e Magic (un valore mana memorizzato; il danno è strength * 3 quando mana è almeno 10 e 1 altrimenti; damage non modifica mana). Hero memorizza il proprio strength e attack, e strike() restituisce Ada hits with sword for 20.
Il codice fornito legge il nome e la forza dell'eroe, poi i comandi equip sword, equip bow, equip magic 15 o strike. Stampa il risultato di ogni attacco.
Il tuo codice va inserito in Attack.swift, Sword.swift, Bow.swift, Magic.swift e Hero.swift. main.swift contiene il codice di input/output fornito e non può essere modificato.
Provalo tu
// Codice di input/output fornito: lascialo così com'è
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()
}
}
Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.
Tutte le lezioni di Programmazione orientata agli oggetti
1Classi e oggetti
Lavorare con i fileDefinire le classiMetodi e selfInizializzatoriLe classi sono riferimentiRiepilogo - Tessera della biblioteca4Incapsulamento
Controllo degli accessiSola lettura dall’esternoProteggere lo statoInizializzatori che possono fallireRiepilogo - Conto bancario7Polimorfismo e tipi generici
PolimorfismoCasting dei tipiFunzioni genericheTipi genericiRipasso - Calcolatore di forme2Struct e tipi valore
Le structSemantica dei valoriMetodi mutantiStruct o classeRiepilogo - Articolo di inventario5Ereditarietà
SottoclassiSovrascrivere i metodiInizializzatori e superfinal e gerarchieRiepilogo - Gerarchia dei dipendenti8Protocolli standard
CustomStringConvertibleEquatableComparableHashableRiepilogo - Ordinamento personalizzato11Progetto: Gestione della biblioteca
Libri e utentiPrestito dei libriEsercitati da solo: Playground Swift