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 de ellos y los hace intercambiables. Puedes cambiar de algoritmo en tiempo de ejecución sin modificar el código del cliente.
Aquí tienes clases de estrategia sencillas 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 de 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 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)
# Usar diferentes estrategias de ordenamiento
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 durante la ejecución. Define distintas 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 wallet_address en el constructor
- El método pay debe imprimir exactamente:
Paying $X using Bitcoin wallet: Y, donde X es el importe y 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 cueste $1200 y unos auriculares que cuesten $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 tu función main() solo se ejecute cuando el archivo se ejecuta directamente (no cuando otro módulo lo importa). 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.
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")
# Create your BitcoinPayment strategy class here
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
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 bancariasPractica por tu cuenta: Compilador de Python online