Patrón Strategy
Parte de la sección Programación orientada a objetos del Journey de GDScript de Coddy. Lección 49 de 65.
El patrón Strategy coloca cada versión de un algoritmo en su propio objeto, para que puedas cambiar el algoritmo mientras se ejecuta el programa. El objeto que lo usa, el contexto, llama a un método y no le importa qué versión contiene.
Aquí tienes dos estrategias de ataque, una clase padre y una subclase con el mismo 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 * 2El contexto almacena una estrategia y le delega el cálculo:
# 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 comprueba qué estilo tiene. Un estilo nuevo es una clase nueva, y fighter.gd se mantiene tal como está; una cadena de if style == "heavy" dentro de attack() crecería con cada estilo.
Cuando una estrategia es una pequeña función sin datos propios, basta con un Callable. El contexto almacena la función y la ejecuta con 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
55Usa clases cuando una estrategia tenga sus propios ajustes o varios métodos, y Callables para un cálculo breve. Ya has usado esta idea antes: la función de comparación que pasas a sort_custom() es una estrategia de ordenamiento.
Desafío
IntermedioUn carrito de compras puede cambiar de estrategia de descuento al finalizar la compra. Discount en discount.gd es la estrategia base: sin descuento. Escribe otras dos estrategias y el carrito:
PercentOff(se crea con un porcentaje):apply(total)devuelvetotal - total * percent / 100(aritmética entera), ylabel()devuelve10% offpara un 10 por ciento.FixedOff(se crea con un importe):apply(total)devuelve el total menos el importe, pero nunca menos de0, ylabel()devuelve30 offpara un importe de 30.Cart:set_discount(new_discount)almacena la estrategia, ycheckout()suma los precios y devuelveSubtotal TOTAL, LABEL, pay PRICE, donde PRICE es lo que devuelveapply()de la estrategia.
El código proporcionado lee los precios en una línea, cuántas compras se procesarán y una estrategia por línea: percent N, fixed N o none. Para los precios 50 30 40 y las estrategias percent 10, fixed 30, none, la salida es:
Subtotal 120, 10% off, pay 108
Subtotal 120, 30 off, pay 90
Subtotal 120, no discount, pay 120Tu código va en cart.gd, percent_off.gd y fixed_off.gd. main.gd contiene el código de entrada/salida proporcionado y no se puede editar.
Pruébalo tú mismo
extends Node
func _ready():
# Código de entrada/salida proporcionado: mantenlo tal 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 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
1Fundamentos de la programación orientada a objetos
Archivos externosIntroducción a la programación orientada a objetosClases vs. objetosLa palabra clave selfMétodosPropiedadesConstructor (_init)Repaso: calculadora sencilla4Polimorfismo
Repaso de la sobrescritura de métodosTipado patoClases y métodos abstractosDiseño de interfacesRepaso: calculadora de formas7Señales
¿Qué es una señal?Declarar y emitirConectarse a las señalesSeñales con argumentosRepaso: sistema de salud10Patrones de diseño, parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón Composite2Propiedades de clase
Variables de instancia y estáticasFunciones estáticasMétodos setter de propiedadesMétodos getter de propiedadesPrivado por convenciónRepaso: administrador de cuentas bancarias5Encapsulación
Miembros públicos y privadosValidación con settersOcultación de informaciónPropiedades de solo lecturaRepaso: sistema de registros de estudiantes8Conceptos avanzados de POO
Composición frente a herenciaClases internasNodos como componentesRefCounted frente a NodeEnums en clases3Herencia
Herencia básicaLa palabra clave superSobrescritura de métodosCadenas de herenciaComprobaciones de tipo con isRepaso: jerarquía de empleados6Métodos especiales
Introducción a los métodos virtualesEl método _to_stringIteradores personalizadosRepaso: lista personalizada9Patrones de diseño, parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón ObserverPatrón StrategyPractica por tu cuenta: Compilador de GDScript online