Problema

Cambiar algoritmos dinámicamente según el contexto, evitando condicionales complejas.

Propósito

Encapsula diferentes algoritmos en clases separadas y permite intercambiarlos en tiempo de ejecución. El cliente elige qué algoritmo usar.

Concepto clave

Algoritmo intercambiable: Como cambiar el método de pago en una tienda online - el proceso es el mismo pero el algoritmo de pago cambia (tarjeta, PayPal, transferencia).

En términos simples: Cambiar el “cómo” sin cambiar el “qué”. El Context siempre hace lo mismo (calcular precio), pero la Strategy define cómo lo hace.

Casos de uso comunes

  • Algoritmos de pricing (descuentos, promociones)
  • Métodos de pago (tarjeta, PayPal, transferencia)
  • Algoritmos de ordenamiento
  • Estrategias de validación
  • Algoritmos de compresión
  • Estrategias de caching

¿Quién es quién en Strategy?

Actor Lo que realmente es Ejemplo Analogía
Context Usa las estrategias, mantiene referencia actual PricingContext - siempre calcula precios Calculadora de precios
Strategy Interfaz que define qué pueden hacer PricingStrategy - define calculatePrice() “Método de cálculo” (interfaz)
ConcreteStrategy Implementaciones que hacen el trabajo diferente VIPPricingStrategy, SeasonalPricingStrategy Descuento VIP, descuento temporal

Diagrama

classDiagram
    namespace StrategyPattern {
        class Context {
            -strategy: Strategy
            +setStrategy(strategy)
            +executeStrategy()
        }
        
        class Strategy {
            <<interface>>
            +execute(data) Result
        }
        
        class ConcreteStrategyA {
            +execute(data) Result
        }
        
        class ConcreteStrategyB {
            +execute(data) Result
        }
        
        class ConcreteStrategyC {
            +execute(data) Result
        }
    }
    
    Context --> Strategy
    Strategy <|.. ConcreteStrategyA
    Strategy <|.. ConcreteStrategyB
    Strategy <|.. ConcreteStrategyC

Ejemplo práctico

classDiagram
    namespace PricingExample {
        class PricingContext {
            -strategy: PricingStrategy
            +setStrategy(strategy)
            +calculatePrice(order) BigDecimal
        }
        
        class PricingStrategy {
            <<interface>>
            +calculatePrice(order) BigDecimal
        }
        
        class RegularPricingStrategy {
            +calculatePrice(order) BigDecimal
        }
        
        class VIPPricingStrategy {
            -discountPercentage: double
            +calculatePrice(order) BigDecimal
        }
        
        class SeasonalPricingStrategy {
            -seasonMultiplier: double
            +calculatePrice(order) BigDecimal
        }
        
        class BulkPricingStrategy {
            -bulkThreshold: int
            -bulkDiscount: double
            +calculatePrice(order) BigDecimal
        }
        
        class PricingFactory {
            +createStrategy(customerType, season, quantity) PricingStrategy
        }
    }
    
    PricingContext --> PricingStrategy
    PricingStrategy <|.. RegularPricingStrategy
    PricingStrategy <|.. VIPPricingStrategy
    PricingStrategy <|.. SeasonalPricingStrategy
    PricingStrategy <|.. BulkPricingStrategy
    PricingFactory --> PricingStrategy

Selección de estrategia

flowchart TD
    A[Order Request] --> B{Customer Type?}
    B -->|VIP| C[VIP Strategy]
    B -->|Regular| D{Season?}
    B -->|Corporate| E{Quantity?}
    
    D -->|Holiday| F[Seasonal Strategy]
    D -->|Normal| G[Regular Strategy]
    
    E -->|> 100| H[Bulk Strategy]
    E -->|< 100| I[Corporate Strategy]
    
    C --> J[Calculate Price]
    F --> J
    G --> J
    H --> J
    I --> J

Flujo de ejecución

sequenceDiagram
    participant Client
    participant Context
    participant Factory
    participant Strategy
    
    Note over Client,Factory: 1. Factory crea la estrategia según contexto
    Client->>Factory: createStrategy(customerType, conditions)
    Factory->>Strategy: new ConcreteStrategy()
    Factory-->>Client: strategy
    
    Note over Client,Context: 2. Context recibe y usa la estrategia
    Client->>Context: setStrategy(strategy)
    Client->>Context: executeStrategy(order)
    Context->>Strategy: execute(order)
    Strategy->>Strategy: apply algorithm
    Strategy-->>Context: result
    Context-->>Client: result
    
    Note over Client,Strategy: El Factory decide QUÉ estrategia - El Context ejecuta CÓMO funciona

Ventajas

  • Flexibilidad: Algoritmos intercambiables en tiempo de ejecución
  • Extensibilidad: Fácil agregar nuevas estrategias
  • Eliminación de condicionales: No más if/switch complejos
  • Testabilidad: Cada estrategia se puede probar independientemente

Desventajas

  • Complejidad: Introduce más clases
  • Conocimiento del cliente: El cliente debe conocer las diferentes estrategias
  • Comunicación: Todas las estrategias deben usar la misma interfaz
  • Overhead: Puede ser excesivo para algoritmos simples

Cuándo usar

  • Tienes múltiples formas de realizar la misma tarea
  • Quieres cambiar algoritmos dinámicamente
  • Tienes muchas condicionales relacionadas con algoritmos
  • Diferentes clientes necesitan diferentes variantes de un algoritmo

Cuándo NO usar

  • Solo tienes un algoritmo
  • Los algoritmos nunca cambian
  • Las diferencias entre algoritmos son mínimas
  • La complejidad adicional no se justifica

Diferencias con otros patrones

  • vs State: Strategy se cambia externamente por el cliente, State cambia automáticamente según estado interno
  • vs Template Method: Strategy cambia todo el algoritmo, Template Method solo algunos pasos del algoritmo
  • vs Factory: Factory decide QUÉ crear, Strategy define CÓMO ejecutar