Menu
CoddyTech

Patrón Strategy

Parte de la sección Programación Orientada a Objetos del Journey de C++ de Coddy. Lección 95 de 104.

El patrón Strategy define una familia de algoritmos, encapsula cada uno y los hace intercambiables. Esto te permite cambiar el comportamiento de un objeto en tiempo de ejecución sin modificar su código: el algoritmo varía independientemente de los clientes que lo utilizan.

El patrón consta de tres partes: una interfaz de estrategia que declara el método del algoritmo, estrategias concretas que implementan distintas variantes y un contexto que utiliza una estrategia:

#include <iostream>
#include <memory>

// Interfaz de estrategia
class PaymentStrategy {
public:
    virtual void pay(int amount) = 0;
    virtual ~PaymentStrategy() = default;
};

// Estrategias concretas
class CreditCardPayment : public PaymentStrategy {
public:
    void pay(int amount) override {
        std::cout << "Paid " << amount << " via Credit Card\n";
    }
};

class PayPalPayment : public PaymentStrategy {
public:
    void pay(int amount) override {
        std::cout << "Paid " << amount << " via PayPal\n";
    }
};

// Contexto
class ShoppingCart {
    std::unique_ptr<PaymentStrategy> strategy;
public:
    void setPaymentMethod(std::unique_ptr<PaymentStrategy> s) {
        strategy = std::move(s);
    }
    
    void checkout(int total) {
        if (strategy) strategy->pay(total);
    }
};

int main() {
    ShoppingCart cart;
    
    cart.setPaymentMethod(std::make_unique<CreditCardPayment>());
    cart.checkout(100);
    
    cart.setPaymentMethod(std::make_unique<PayPalPayment>());
    cart.checkout(50);
}

ShoppingCart no sabe qué método de pago está usando; simplemente llama a pay() en la estrategia que esté configurada. Puedes cambiar de estrategia en tiempo de ejecución con setPaymentMethod(), lo que hace que el sistema sea flexible y fácil de ampliar con nuevas opciones de pago.

Usa Strategy cuando tengas varios algoritmos para una tarea específica y quieras alternar entre ellos dinámicamente, o cuando quieras evitar las instrucciones condicionales para seleccionar el comportamiento.

challenge icon

Desafío

Fácil

Construyamos una calculadora de envíos que use el patrón Strategy para calcular los costos de entrega según distintos métodos de envío. Este es un caso práctico en el que necesitas intercambiar algoritmos en tiempo de ejecución: un mismo paquete podría enviarse por tierra, por aire o por envío exprés, cada uno con su propia lógica de precios.

Organizarás tu código en tres archivos:

  • ShippingStrategy.h: Define la interfaz de estrategia y las estrategias de envío concretas.

    Crea una clase abstracta ShippingStrategy con un método virtual puro calculateCost(double weight) que devuelva el costo de envío como un double, junto con un destructor virtual.

    Después, implementa tres estrategias concretas:

    • GroundShipping: cuesta 1.5 por unidad de peso (weight * 1.5)
    • AirShipping: cuesta 4.0 por unidad de peso (weight * 4.0)
    • ExpressShipping: cuesta 6.5 por unidad de peso más una tarifa fija de 10.0 (weight * 6.5 + 10.0)
  • ShippingService.h: Crea la clase de contexto que usa una estrategia de envío.

    Tu clase ShippingService debe contener un std::unique_ptr<ShippingStrategy> como miembro privado. Implementa:

    • Un método setStrategy(std::unique_ptr<ShippingStrategy> strategy) para cambiar el método de envío
    • Un método calculateShipping(double weight) que use la estrategia actual para calcular y devolver el costo

    Si no se ha establecido ninguna estrategia cuando se llama a calculateShipping, devuelve 0.0.

  • main.cpp: Demuestra cómo cambiar de estrategia en tiempo de ejecución.

    Lee dos entradas:

    1. Peso del paquete (double)
    2. Método de envío: ground, air o express

    Crea un ShippingService y establece la estrategia adecuada según el método de entrada. Calcula e imprime el costo de envío.

    Después, cambia a una estrategia diferente (usa air si la entrada no era air; de lo contrario, usa ground) y vuelve a calcular el costo para el mismo peso. Esto demuestra las ventajas de intercambiar estrategias en tiempo de ejecución.

    Imprime cada costo en su propia línea con exactamente un decimal, precedido por el nombre del método:

    [Method]: $[cost]

Por ejemplo, con las entradas 5.0 y ground:

Ground: $7.5
Air: $20.0

Con las entradas 3.0 y express:

Express: $29.5
Air: $12.0

Con las entradas 10.0 y air:

Air: $40.0
Ground: $15.0

Observa que ShippingService no necesita conocer los detalles de cada algoritmo de precios. Simplemente delega la tarea a la estrategia que esté establecida en ese momento. Puedes añadir fácilmente nuevos métodos de envío (como entrega con drones o en el mismo día) sin modificar en absoluto la clase del servicio.

Pruébalo tú mismo

#include <iostream>
#include <string>
#include <iomanip>
#include <memory>
#include "ShippingStrategy.h"
#include "ShippingService.h"

int main() {
    double weight;
    std::string method;
    
    std::cin >> weight;
    std::cin >> method;
    
    // Configura la salida con 1 decimal
    std::cout << std::fixed << std::setprecision(1);
    
    ShippingService service;
    
    // TODO: Según el método introducido ("ground", "air" o "express"):
    // 1. Establece la estrategia adecuada en el servicio
    // 2. Calcula e imprime el costo con el formato: "[Method]: $[cost]"
    
    // TODO: Cambia a una estrategia diferente:
    // - Si la entrada fue "air", cambia a GroundShipping
    // - De lo contrario, cambia a AirShipping
    // Calcula e imprime el nuevo costo
    
    return 0;
}
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 Programación Orientada a Objetos

Practica por tu cuenta: Compilador de C++ online