Patrón Strategy
Parte de la sección Programación orientada a objetos del Journey de Python de Coddy. Lección 48 de 64.
El patrón Strategy define una familia de algoritmos, encapsula cada uno y permite intercambiarlos. Puedes cambiar de algoritmo durante la ejecución sin modificar el código cliente.
Aquí tienes clases sencillas de estrategia para distintos 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 use 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 estrategia:
cart = ShoppingCart()
cart.add_item(50)
cart.add_item(30)
# Usa la estrategia de tarjeta de crédito
cart.set_payment_strategy(CreditCard())
print(cart.checkout())
# Cambia a la estrategia de PayPal
cart.set_payment_strategy(PayPal())
print(cart.checkout())Crea 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)
# 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))Salida:
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 te permite intercambiar algoritmos en tiempo de ejecución. Define diferentes estrategias con la misma interfaz y, después, deja que la clase de contexto elija cuál usar. Esto hace que tu código sea flexible y fácil de ampliar con nuevos algoritmos sin cambiar el código existente.
Desafío
IntermedioImplementa una nueva estrategia BitcoinPayment para nuestro sistema de compras.
Tu tarea es:
- Crea 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: Y, donde X es el importe e Y es la dirección de la cartera - El método pay debe devolver True después de imprimir
- Escribe una función main que:
- Cree un carrito de compras
- Añada al carrito un portátil que cuesta $1200 y unos auriculares que cuestan $100
- Cree un BitcoinPayment con la dirección de la cartera "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa"
- Establezca esta estrategia de pago en el carrito
- Llame al método checkout
El código inicial ya incluye if __name__ == "__main__": al final. No necesitas añadirlo tú. Esta es una convención de Python que garantiza que la función main() solo se ejecute cuando el archivo se ejecuta directamente (no cuando otro módulo lo importa). Simplemente define la 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.
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 aquí tu clase de estrategia BitcoinPayment
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 Programación orientada a objetos
1Fundamentos de la POO
Introducción a la POOArchivos externosClases vs. objetosEl parámetro selfMétodosAtributosMétodo constructor (__init__)Repaso: calculadora sencilla4Herencia
Herencia básicaLa función super()Sobrescritura de métodosHerencia múltipleOrden de resolución de métodosRepaso: 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 de propiedadDecorador de método estáticoDecorador de método de clase5Polimorfismo
Repaso de la sobrescritura de métodosTipado patoClases y métodos abstractosDiseño de interfacesRepaso: calculadora de formas8Conceptos avanzados de POO
Composición frente a 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. variables de claseDecoradores de propiedadesAtributos privadosRepaso: gestor de cuentas bancariasPractica por tu cuenta: Compilador de Python online