Padrão Strategy
Parte da seção Programação orientada a objetos do Journey de GDScript da Coddy. Lição 49 de 65.
O padrão Strategy coloca cada versão de um algoritmo em seu próprio objeto, para que você possa trocar o algoritmo enquanto o programa está em execução. O objeto que o utiliza, o contexto, chama um método e não se importa com qual versão está usando.
Aqui estão duas estratégias de ataque, uma classe pai e uma subclasse com o mesmo método damage():
# attack_style.gd
class_name AttackStyle
extends RefCounted
func damage(power: int) -> int:
return power# heavy_attack.gd
class_name HeavyAttack
extends AttackStyle
func damage(power: int) -> int:
return power * 2O contexto armazena uma estratégia e delega o cálculo a ela:
# fighter.gd
class_name Fighter
extends RefCounted
var power := 10
var style: AttackStyle = AttackStyle.new()
func set_style(new_style: AttackStyle) -> void:
style = new_style
func attack() -> void:
print("Hit for ", style.damage(power))# main.gd
var fighter := Fighter.new()
fighter.attack()
fighter.set_style(HeavyAttack.new())
fighter.attack()Hit for 10
Hit for 20Fighter nunca verifica qual estilo possui. Um novo estilo é uma nova classe, e fighter.gd permanece como está; uma cadeia de if style == "heavy" dentro de attack() cresceria a cada estilo.
Quando uma estratégia é uma pequena função sem dados próprios, um Callable é suficiente. O contexto armazena a função e a executa com call():
# price_calculator.gd
class_name PriceCalculator
extends RefCounted
var discount: Callable = func(price): return price
func final_price(price: int) -> int:
return discount.call(price)# main.gd
var calc := PriceCalculator.new()
print(calc.final_price(80))
calc.discount = func(price): return price / 2
print(calc.final_price(80))
calc.discount = func(price): return max(price - 25, 0)
print(calc.final_price(80))80
40
55Use classes quando uma estratégia tiver suas próprias configurações ou vários métodos, e Callables para um cálculo curto. Você já usou essa ideia antes: a função de comparação que você passa para sort_custom() é uma estratégia de ordenação.
Desafio
MédioUm carrinho de compras pode alternar entre estratégias de desconto no checkout. Discount em discount.gd é a estratégia base: sem desconto. Escreva mais duas e o carrinho:
PercentOff(criado com uma porcentagem):apply(total)retornatotal - total * percent / 100(aritmética inteira), elabel()retorna10% offpara 10 por cento.FixedOff(criado com um valor):apply(total)retorna o total menos o valor, mas nunca abaixo de0, elabel()retorna30 offpara um valor de 30.Cart:set_discount(new_discount)armazena a estratégia, echeckout()soma os preços e retornaSubtotal TOTAL, LABEL, pay PRICE, em que PRICE é o valor retornado porapply()da estratégia.
O código fornecido lê os preços em uma linha, quantos checkouts vêm a seguir e uma estratégia por linha: percent N, fixed N ou none. Para os preços 50 30 40 e as estratégias percent 10, fixed 30, none, a saída é:
Subtotal 120, 10% off, pay 108
Subtotal 120, 30 off, pay 90
Subtotal 120, no discount, pay 120Seu código deve ficar em cart.gd, percent_off.gd e fixed_off.gd. main.gd contém o código fornecido de entrada/saída e não pode ser editado.
Experimente você mesmo
extends Node
func _ready():
# Código de entrada/saída fornecido: mantenha-o como está
var cart := Cart.new()
for price in OS.read_string_from_stdin().split(" "):
cart.add_item(int(price))
var count := int(OS.read_string_from_stdin())
for i in count:
var parts := OS.read_string_from_stdin().split(" ")
if parts[0] == "percent":
cart.set_discount(PercentOff.new(int(parts[1])))
elif parts[0] == "fixed":
cart.set_discount(FixedOff.new(int(parts[1])))
else:
cart.set_discount(Discount.new())
print(cart.checkout())
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
1Fundamentos da POO
Arquivos externosIntrodução à POOClasses vs. objetosA palavra-chave selfMétodosPropriedadesConstrutor (_init)Revisão - Calculadora simples4Polimorfismo
Revisitando a Sobrescrita de MétodosDuck TypingClasses e Métodos AbstratosDesign de InterfacesRevisão - Calculadora de Formas7Sinais
O que é um sinal?Declarando e emitindoConectando-se a sinaisSinais com argumentosRevisão - Sistema de saúde10Padrões de Projeto Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão Composite2Propriedades de Classe
Variáveis de Instância vs. EstáticasFunções EstáticasMétodos Setters de PropriedadeMétodos Getters de PropriedadePrivado por ConvençãoRecapitulando: Gerenciador de Conta Bancária5Encapsulamento
Membros públicos e privadosValidação com settersOcultação de informaçõesPropriedades somente para leituraRevisão - Sistema de registros de alunos8Conceitos Avançados de POO
Composição vs. HerançaClasses InternasNós como ComponentesRefCounted vs. NodeEnums em Classes3Herança
Herança básicaA palavra-chave superSobrescrita de métodosCadeias de herançaVerificações de tipo com isRevisão - Hierarquia de funcionários6Métodos especiais
Introdução aos métodos virtuaisO método _to_stringIteradores personalizadosRevisão - Lista personalizada9Padrões de Projeto Parte 1
Introdução aos Padrões de ProjetoPadrão SingletonPadrão FactoryPadrão ObserverPadrão StrategyPratique por conta própria: Compilador de GDScript online