Introducción

El patrón de diseño Strategy es un patrón de comportamiento que permite definir una familia de algoritmos, encapsular cada uno de ellos y hacerlos intercambiables. Este patrón permite que el algoritmo varíe independientemente de los clientes que lo utilizan.

Conceptos Clave

  • Contexto: La clase que utiliza una estrategia.
  • Estrategia: La interfaz común para todas las estrategias concretas.
  • Estrategia Concreta: Implementaciones específicas de la estrategia.

Estructura del Patrón Strategy

La estructura del patrón Strategy se puede representar mediante el siguiente diagrama UML:

+----------------+        +----------------+
|    Context     |        |   Strategy     |
|----------------|        |----------------|
| - strategy:    |<>------| + algorithm()  |
|   Strategy     |        +----------------+
|----------------|        |                |
| + setStrategy()|        |                |
| + execute()    |        |                |
+----------------+        +----------------+
        |                          ^
        |                          |
        v                          |
+----------------+        +----------------+
| ConcreteStrategyA |        | ConcreteStrategyB |
|----------------|        |----------------|
| + algorithm()  |        | + algorithm()  |
+----------------+        +----------------+

Componentes

  1. Context: Mantiene una referencia a una estrategia concreta y delega el comportamiento a la estrategia.
  2. Strategy: Interfaz común para todas las estrategias concretas.
  3. ConcreteStrategy: Implementaciones específicas de la interfaz Strategy.

Ejemplo Práctico

Vamos a implementar un ejemplo en Python donde un contexto (una calculadora) puede utilizar diferentes estrategias para realizar operaciones matemáticas.

Código

from abc import ABC, abstractmethod

# Definición de la interfaz Strategy
class Strategy(ABC):
    @abstractmethod
    def execute(self, a: int, b: int) -> int:
        pass

# Implementación de estrategias concretas
class AddStrategy(Strategy):
    def execute(self, a: int, b: int) -> int:
        return a + b

class SubtractStrategy(Strategy):
    def execute(self, a: int, b: int) -> int:
        return a - b

class MultiplyStrategy(Strategy):
    def execute(self, a: int, b: int) -> int:
        return a * b

class DivideStrategy(Strategy):
    def execute(self, a: int, b: int) -> int:
        if b == 0:
            raise ValueError("Cannot divide by zero")
        return a // b

# Definición del Contexto
class Calculator:
    def __init__(self, strategy: Strategy):
        self._strategy = strategy

    def set_strategy(self, strategy: Strategy):
        self._strategy = strategy

    def calculate(self, a: int, b: int) -> int:
        return self._strategy.execute(a, b)

# Uso del patrón Strategy
if __name__ == "__main__":
    calculator = Calculator(AddStrategy())
    print("Addition: ", calculator.calculate(10, 5))  # Output: 15

    calculator.set_strategy(SubtractStrategy())
    print("Subtraction: ", calculator.calculate(10, 5))  # Output: 5

    calculator.set_strategy(MultiplyStrategy())
    print("Multiplication: ", calculator.calculate(10, 5))  # Output: 50

    calculator.set_strategy(DivideStrategy())
    print("Division: ", calculator.calculate(10, 5))  # Output: 2

Explicación del Código

  1. Strategy: Es una clase abstracta que define el método execute.
  2. AddStrategy, SubtractStrategy, MultiplyStrategy, DivideStrategy: Son implementaciones concretas de la interfaz Strategy.
  3. Calculator: Es el contexto que utiliza una estrategia concreta para realizar cálculos. Permite cambiar la estrategia en tiempo de ejecución mediante el método set_strategy.

Ejercicio Práctico

Ejercicio

Implementa una estrategia concreta para calcular el módulo de dos números y pruébala utilizando la clase Calculator.

Solución

class ModuloStrategy(Strategy):
    def execute(self, a: int, b: int) -> int:
        return a % b

# Uso del patrón Strategy con ModuloStrategy
if __name__ == "__main__":
    calculator = Calculator(ModuloStrategy())
    print("Modulo: ", calculator.calculate(10, 3))  # Output: 1

Errores Comunes y Consejos

  1. No encapsular el algoritmo: Asegúrate de que cada estrategia concreta encapsule su algoritmo y no dependa de otras estrategias.
  2. No utilizar la interfaz Strategy: Siempre utiliza la interfaz común para definir las estrategias, esto facilita la intercambiabilidad.
  3. No manejar excepciones: En estrategias como la división, asegúrate de manejar casos excepcionales como la división por cero.

Conclusión

El patrón Strategy es una poderosa herramienta para diseñar software flexible y extensible. Permite cambiar el comportamiento de un objeto en tiempo de ejecución sin modificar su código. En el próximo tema, exploraremos el patrón Template Method, que también es un patrón de comportamiento pero con un enfoque diferente.

© Copyright 2024. Todos los derechos reservados