Patrón Strategy
Parte de la sección Programación orientada a objetos del Journey de Swift de Coddy. Lección 45 de 57.
El patrón de estrategia coloca cada variante de un algoritmo en su propio tipo, detrás de un protocolo compartido. El código que lo utiliza mantiene una estrategia y puede cambiarla en tiempo de ejecución:
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))Salida:
108
121En Swift, un cierre suele ser suficiente para una estrategia con un método. El tipo almacena una función y la llama:
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))Salida:
["apple", "banana", "kiwi"]
["kiwi", "apple", "banana"]La estrategia reemplaza un switch dentro del algoritmo. Las nuevas variantes son nuevos tipos (o cierres), y el código que los utiliza permanece sin cambios:
// sin una estrategia: cada nueva opción edita esta función
func cost(kind: String, weight: Int) -> Int {
switch kind { case "standard": return 5 + weight; default: return 15 + weight * 2 }
}
// con una estrategia: Checkout nunca cambia
struct Overnight: ShippingStrategy { func cost(weight: Int) -> Int { 40 } }Las estrategias pueden elegirse a partir de datos, por ejemplo, de una configuración del usuario, mediante una pequeña fábrica:
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))Salida:
160
200Desafío
FácilEl ataque de un personaje del juego es una estrategia. El protocolo Attack tiene name y damage(strength:) -> Int. Implementa Sword (strength * 2), Bow (strength + 5) y Magic (un mana almacenado; el daño es strength * 3 cuando mana es al menos 10 y 1 en caso contrario; damage no modifica mana). Hero almacena su strength y attack, y strike() devuelve Ada hits with sword for 20.
El código proporcionado lee el nombre y la fuerza del héroe, después los comandos equip sword, equip bow, equip magic 15 o strike. Imprime el resultado de cada ataque.
Tu código va en Attack.swift, Sword.swift, Bow.swift, Magic.swift y Hero.swift. main.swift contiene el código proporcionado de entrada/salida y no se puede editar.
Pruébalo tú mismo
// Código de entrada/salida suministrado: déjalo tal como está
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()
}
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación orientada a objetos
1Clases y objetos
Trabajar con archivosDefinir clasesMétodos y selfInicializadoresLas clases son referenciasRepaso: tarjeta de biblioteca4Encapsulación
Control de accesoSolo lectura desde fueraProtección del estadoInicializadores que pueden fallarRepaso - Cuenta bancaria7Polimorfismo y genéricos
PolimorfismoConversión de tiposFunciones genéricasTipos genéricosRepaso - Calculadora de formas2Structs y tipos por valor
StructsSemántica de valoresMétodos mutablesStruct o claseRepaso - elemento del inventario5Herencia
SubclasesSobrescritura de métodosInicializadores y superfinal y jerarquíasRepaso: jerarquía de empleados8Protocolos estándar
CustomStringConvertibleEquatableComparableHashableRepaso - Ordenamiento personalizado11Proyecto: Gestión de bibliotecas
Libros y miembrosPréstamo de librosPractica por tu cuenta: Playground de Swift