Menu
Coddy logo textTech

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.

challenge icon

Desafío

Intermedio

Implementa una nueva estrategia BitcoinPayment para nuestro sistema de compras.

Tu tarea es:

  1. 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: Y donde X es el monto y Y es la dirección de la billetera
    • El método pay debe devolver True después de imprimir
  2. 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()
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Object Oriented Programming