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.
Desafío
FácilConstruyamos 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
ShippingStrategycon un método virtual purocalculateCost(double weight)que devuelva el costo de envío como un double, junto con un destructor virtual.Después, implementa tres estrategias concretas:
GroundShipping: cuesta1.5por unidad de peso (weight * 1.5)AirShipping: cuesta4.0por unidad de peso (weight * 4.0)ExpressShipping: cuesta6.5por unidad de peso más una tarifa fija de10.0(weight * 6.5 + 10.0)
ShippingService.h: Crea la clase de contexto que usa una estrategia de envío.Tu clase
ShippingServicedebe contener unstd::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, devuelve0.0.- Un método
main.cpp: Demuestra cómo cambiar de estrategia en tiempo de ejecución.Lee dos entradas:
- Peso del paquete (double)
- Método de envío:
ground,airoexpress
Crea un
ShippingServicey 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
airsi la entrada no eraair; de lo contrario, usaground) 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.0Con las entradas 3.0 y express:
Express: $29.5
Air: $12.0Con las entradas 10.0 y air:
Air: $40.0
Ground: $15.0Observa 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;
}
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
1Fundamentos de la POO
Archivos externosCompilación y construcción en C++Archivos de cabecera y archivos fuenteEspacios de nombres y ámbitoIntroducción a la POO en C++Clases y objetosEl puntero 'this'Métodos (funciones miembro)Atributos (miembros de datos)Fundamentos de constructores y destructoresRepaso: calculadora sencilla4Propiedades de clase
Miembros de instancia vs. estáticosGetters y settersFunciones miembro constPalabra clave mutableMétodos y variables estáticosFunciones y clases friendRepaso: gestor de cuentas bancarias7Herencia
Herencia básicaNiveles de acceso en la herenciaOrden de llamada de constructores y destructoresSobrescritura de métodosFunciones virtuales y VTableHerencia múltipleHerencia virtualRepaso: jerarquía de empleados10Visión general de STL
Visión general y filosofía de STLContenedores de STLIteradoresAlgoritmos de STLFunctores y expresiones lambdaRepaso: frecuencia de palabras13Patrones de diseño, parte 1
Introducción a los patrones de diseñoPatrón SingletonFactory y Abstract FactoryPatrón BuilderPatrón ObserverPatrón Strategy2Gestión de memoria
Memoria de pila vs. heapPunteros y referenciasMemoria dinámica (new/delete)Punteros inteligentes en C++RAII en C++Repaso: gestor de arreglos dinámicos5Encapsulación
Especificadores de acceso en C++Especificadores de acceso en profundidadOcultación de informaciónStruct vs. classClases anidadas e internasRepaso: sistema de registros de estudiantes8Polimorfismo
Polimorfismo en compilación y en tiempo de ejecuciónSobrecarga de funcionesRepaso de las funciones virtualesFunciones virtuales purasClases abstractasDiseño de interfaces en C++Conversión dinámica y RTTIRepaso: calculadora de formas11Conceptos avanzados de POO
Composición frente a herenciaMixins mediante CRTPIdiom PimplBorrado de tiposClases enum y tipado fuerteManejo de excepciones en POOJerarquías de excepciones personalizadas14Patrones de diseño, parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón CompositeRAII como patrón3Constructores y destructores
Constructor predeterminadoConstructor con parámetrosConstructor de copiaConstructor de movimientoListas de inicialización del constructorConstructores delegadosAnálisis detallado del destructorRegla de tres / cinco / ceroRepaso: clase String6Sobrecarga de operadores
Introducción a la sobrecarga de operadoresSobrecarga de operadores aritméticosSobrecarga de operadores de comparaciónOperadores de flujoSobrecarga del operador de asignaciónSobrecarga de los operadores [] y ()Operadores de conversión de tipoRepaso: clase Matrix9Plantillas
Plantillas de funcionesPlantillas de clasesEspecialización de plantillasPlantillas variádicasFundamentos de SFINAE y rasgos de tiposRepaso: contenedor genéricoPractica por tu cuenta: Compilador de C++ online