Padrão Strategy
Parte da seção Programação Orientada a Objetos do Journey de Swift da Coddy. Lição 45 de 57.
O padrão strategy coloca cada variante de um algoritmo em seu próprio tipo, por trás de um protocolo compartilhado. O código que o utiliza mantém uma estratégia e pode trocá-la em tempo de execução:
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))Saída:
108
121Em Swift, uma closure geralmente é suficiente para uma estratégia com um método. O tipo armazena uma função e a chama:
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))Saída:
["apple", "banana", "kiwi"]
["kiwi", "apple", "banana"]A estratégia substitui um switch dentro do algoritmo. Novas variantes são novos tipos (ou closures), e o código que os utiliza permanece inalterado:
// sem uma estratégia: cada nova opção edita esta função
func cost(kind: String, weight: Int) -> Int {
switch kind { case "standard": return 5 + weight; default: return 15 + weight * 2 }
}
// com uma estratégia: Checkout nunca muda
struct Overnight: ShippingStrategy { func cost(weight: Int) -> Int { 40 } }As estratégias podem ser escolhidas a partir de dados, por exemplo, de uma configuração do usuário, com uma pequena 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))Saída:
160
200Desafio
FácilO ataque de um personagem de jogo é uma estratégia. O protocolo Attack tem name e damage(strength:) -> Int. Implemente Sword (strength * 2), Bow (strength + 5) e Magic (um mana armazenado; dano strength * 3 quando mana é pelo menos 10 e 1 caso contrário; o próprio damage não altera mana). Hero armazena seu strength e attack, e strike() retorna Ada hits with sword for 20.
O código fornecido lê o nome e a strength do herói e, em seguida, os comandos equip sword, equip bow, equip magic 15 ou strike. Ele imprime o resultado de cada strike.
Seu código deve estar em Attack.swift, Sword.swift, Bow.swift, Magic.swift e Hero.swift. main.swift contém o código fornecido de entrada/saída e não pode ser editado.
Experimente você mesmo
// Código de entrada/saída fornecido: mantenha-o 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 lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Programação Orientada a Objetos
1Classes e Objetos
Trabalhando com ArquivosDefinindo ClassesMétodos e selfInicializadoresClasses são ReferênciasRevisão - Cartão da Biblioteca4Encapsulamento
Controle de AcessoSomente Leitura ExternaProtegendo o EstadoInicializadores que Podem FalharRevisão - Conta Bancária7Polimorfismo e Genéricos
PolimorfismoConversão de TiposFunções GenéricasTipos GenéricosRevisão - Calculadora de Formas2Structs e Tipos de Valor
StructsSemântica de ValorMétodos MutáveisStruct ou ClasseRecapitulação - Item de Inventário5Herança
SubclassesSobrescrita de métodosInicializadores e superfinal e hierarquiasRecapitulação - Hierarquia de funcionários8Protocolos Padrão
CustomStringConvertibleEquatableComparableHashableRecapitulação - Ordenação Personalizada11Projeto: Gerenciamento de Biblioteca
Livros e MembrosEmpréstimo de LivrosPratique por conta própria: Playground de Swift