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
- Context: Mantiene una referencia a una estrategia concreta y delega el comportamiento a la estrategia.
- Strategy: Interfaz común para todas las estrategias concretas.
- 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
- Strategy: Es una clase abstracta que define el método
execute
. - AddStrategy, SubtractStrategy, MultiplyStrategy, DivideStrategy: Son implementaciones concretas de la interfaz
Strategy
. - 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
- No encapsular el algoritmo: Asegúrate de que cada estrategia concreta encapsule su algoritmo y no dependa de otras estrategias.
- No utilizar la interfaz Strategy: Siempre utiliza la interfaz común para definir las estrategias, esto facilita la intercambiabilidad.
- 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.
Curso de Patrones de Diseño de Software
Módulo 1: Introducción a los Patrones de Diseño
- ¿Qué son los Patrones de Diseño?
- Historia y Origen de los Patrones de Diseño
- Clasificación de los Patrones de Diseño
- Ventajas y Desventajas de Usar Patrones de Diseño
Módulo 2: Patrones Creacionales
- Introducción a los Patrones Creacionales
- Singleton
- Factory Method
- Abstract Factory
- Builder
- Prototype
Módulo 3: Patrones Estructurales
Módulo 4: Patrones de Comportamiento
- Introducción a los Patrones de Comportamiento
- Chain of Responsibility
- Command
- Interpreter
- Iterator
- Mediator
- Memento
- Observer
- State
- Strategy
- Template Method
- Visitor
Módulo 5: Aplicación de Patrones de Diseño
- Cómo Seleccionar el Patrón Adecuado
- Ejemplos Prácticos de Uso de Patrones
- Patrones de Diseño en Proyectos Reales
- Refactorización Usando Patrones de Diseño
Módulo 6: Patrones de Diseño Avanzados
- Patrones de Diseño en Arquitecturas Modernas
- Patrones de Diseño en Microservicios
- Patrones de Diseño en Sistemas Distribuidos
- Patrones de Diseño en Desarrollo Ágil