Patrón Strategy
Parte de la sección Object Oriented Programming del Journey de Python de Coddy — lección 48 de 64.
El patrón Strategy define una familia de algoritmos, encapsula cada uno de ellos y los hace intercambiables. Permite cambiar los algoritmos en tiempo de ejecución sin modificar el código del cliente.
Aquí hay clases de estrategia simples para diferentes métodos de pago:
class CreditCard:
def pay(self, amount):
return f"Paid ${amount} with Credit Card"
class PayPal:
def pay(self, amount):
return f"Paid ${amount} with PayPal"
class Bitcoin:
def pay(self, amount):
return f"Paid ${amount} with Bitcoin"Cada estrategia implementa el mismo método (pay) pero con un comportamiento diferente.
Crea una clase de contexto que utilice estrategias:
class ShoppingCart:
def __init__(self):
self.total = 0
self.payment_strategy = None
def add_item(self, price):
self.total += price
def set_payment_strategy(self, strategy):
self.payment_strategy = strategy
def checkout(self):
return self.payment_strategy.pay(self.total)La clase de contexto puede cambiar entre diferentes estrategias de pago.
Usa el patrón strategy:
cart = ShoppingCart()
cart.add_item(50)
cart.add_item(30)
# Usar la estrategia de tarjeta de crédito
cart.set_payment_strategy(CreditCard())
print(cart.checkout())
# Cambiar a la estrategia de PayPal
cart.set_payment_strategy(PayPal())
print(cart.checkout())Crea otro ejemplo con estrategias de ordenación:
class BubbleSort:
def sort(self, data):
return f"Bubble sorted: {sorted(data)}"
class QuickSort:
def sort(self, data):
return f"Quick sorted: {sorted(data)}"
class Sorter:
def __init__(self, strategy):
self.strategy = strategy
def set_strategy(self, strategy):
self.strategy = strategy
def sort_data(self, data):
return self.strategy.sort(data)
# Usa diferentes estrategias de ordenación
numbers = [3, 1, 4, 1, 5]
sorter = Sorter(BubbleSort())
print(sorter.sort_data(numbers))
sorter.set_strategy(QuickSort())
print(sorter.sort_data(numbers))Resultado:
Paid $80 with Credit Card
Paid $80 with PayPal
Bubble sorted: [1, 1, 3, 4, 5]
Quick sorted: [1, 1, 3, 4, 5]Punto clave: El patrón Strategy le permite intercambiar algoritmos en tiempo de ejecución. Defina diferentes estrategias con la misma interfaz y, a continuación, deje que la clase de contexto elija cuál utilizar. Esto hace que su código sea flexible y fácil de extender con nuevos algoritmos sin cambiar el código existente.
Desafío
IntermedioImplementa una nueva estrategia BitcoinPayment para nuestro sistema de compras.
Tu tarea es:
- Crear una clase BitcoinPayment que implemente PaymentStrategy
- Debe aceptar un wallet_address en el constructor
- El método pay debe imprimir exactamente:
Paying $X using Bitcoin wallet: Ydonde X es el monto y Y es la dirección de la billetera - El método pay debe devolver True después de imprimir
- Escribir una función main que:
- Cree un carrito de compras
- Agregue un laptop que cuesta $1200 y unos headphones que cuestan $100 al carrito
- Cree un BitcoinPayment con la dirección de billetera "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa"
- Establezca esta estrategia de pago en el carrito
- Llame al método checkout
El código inicial ya incluye if __name__ == "__main__": en la parte inferior; no es necesario que lo agregues tú mismo. Esta es una convención de Python que asegura que tu función main() solo se ejecute cuando el archivo se ejecuta directamente (no cuando es importado por otro módulo). Simplemente define tu función main() y el bloque proporcionado la llamará automáticamente.
Sigue la misma estructura que se muestra en los ejemplos de la lección.
Hoja de referencia
El patrón Strategy define una familia de algoritmos, encapsula cada uno de ellos y los hace intercambiables. Permite cambiar de algoritmo en tiempo de ejecución sin modificar el código del cliente.
Crea clases de estrategia con la misma interfaz:
class CreditCard:
def pay(self, amount):
return f"Paid ${amount} with Credit Card"
class PayPal:
def pay(self, amount):
return f"Paid ${amount} with PayPal"
class Bitcoin:
def pay(self, amount):
return f"Paid ${amount} with Bitcoin"Crea una clase de contexto que utilice las estrategias:
class ShoppingCart:
def __init__(self):
self.total = 0
self.payment_strategy = None
def add_item(self, price):
self.total += price
def set_payment_strategy(self, strategy):
self.payment_strategy = strategy
def checkout(self):
return self.payment_strategy.pay(self.total)Utiliza el patrón strategy:
cart = ShoppingCart()
cart.add_item(50)
cart.add_item(30)
# Usar la estrategia de tarjeta de crédito
cart.set_payment_strategy(CreditCard())
print(cart.checkout())
# Cambiar a la estrategia de PayPal
cart.set_payment_strategy(PayPal())
print(cart.checkout())Otro ejemplo con estrategias de ordenamiento:
class BubbleSort:
def sort(self, data):
return f"Bubble sorted: {sorted(data)}"
class QuickSort:
def sort(self, data):
return f"Quick sorted: {sorted(data)}"
class Sorter:
def __init__(self, strategy):
self.strategy = strategy
def set_strategy(self, strategy):
self.strategy = strategy
def sort_data(self, data):
return self.strategy.sort(data)Pruébalo tú mismo
from abc import ABC, abstractmethod
class PaymentStrategy(ABC):
@abstractmethod
def pay(self, amount):
pass
class CreditCardPayment(PaymentStrategy):
def __init__(self, card_number, expiry_date, cvv):
self.card_number = card_number
self.expiry_date = expiry_date
self.cvv = cvv
def pay(self, amount):
print(f"Paying ${amount} using Credit Card: {self.card_number}")
return True
class PayPalPayment(PaymentStrategy):
def __init__(self, email, password):
self.email = email
self.password = password
def pay(self, amount):
print(f"Paying ${amount} using PayPal account: {self.email}")
return True
class ShoppingCart:
def __init__(self):
self.items = []
self.payment_strategy = None
def add_item(self, item, price):
self.items.append({"item": item, "price": price})
def set_payment_strategy(self, payment_strategy):
self.payment_strategy = payment_strategy
def checkout(self):
total = sum(item["price"] for item in self.items)
if self.payment_strategy:
return self.payment_strategy.pay(total)
else:
raise ValueError("No payment strategy set")
# Crea tu clase de estrategia BitcoinPayment aquí
if __name__ == "__main__":
main()Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Object Oriented Programming
1Fundamentos de la POO
Archivos externosIntroducción a la POOClases vs ObjetosEl parámetro selfMétodosAtributosMétodo constructor (__init__)Resumen - Calculadora simple4Herencia
Herencia básicaLa función super()Sobrescritura de métodosHerencia múltipleOrden de resolución de métodosResumen - Jerarquía de empleados7Métodos especiales
Introducción a los métodos mágicosSobrecarga de operadoresMétodos mágicos de contenedoresRepaso - Lista personalizada10Patrones de diseño Parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón ObserverPatrón Strategy2Decoradores
Introducción a los decoradoresDecorador propertyDecorador de métodos estáticosDecorador de métodos de clase5Polimorfismo
Sobreescritura de métodos revisitadaDuck TypingClases y métodos abstractosDiseño de interfacesResumen - Calculadora de figuras8Conceptos avanzados de POO
Composición vs. HerenciaMixinsMétodos estáticos y de claseDecoradores de claseGestores de contexto11Patrones de diseño, parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón Composite3Propiedades de clase
Variables de instancia vs de claseDecoradores de propiedadesAtributos privadosResumen - Gestor de cuentas bancarias