Patrón Strategy
Parte de la sección Programación Orientada a Objetos del Journey de C# de Coddy. Lección 55 de 70.
El patrón Strategy es un patrón de comportamiento que te permite definir una familia de algoritmos, encapsular cada uno en su propia clase y hacer que sean intercambiables en tiempo de ejecución. En lugar de codificar el comportamiento directamente, inyectas la estrategia deseada en un objeto.
El patrón consta de tres partes: una interfaz de estrategia que define el contrato del algoritmo, clases de estrategia concretas que implementan diferentes comportamientos y una clase de contexto que utiliza una estrategia:
public interface IPaymentStrategy
{
void Pay(decimal amount);
}
public class CreditCardPayment : IPaymentStrategy
{
public void Pay(decimal amount) =>
Console.WriteLine($"Paid {amount} via Credit Card");
}
public class PayPalPayment : IPaymentStrategy
{
public void Pay(decimal amount) =>
Console.WriteLine($"Paid {amount} via PayPal");
}
public class ShoppingCart
{
private IPaymentStrategy _paymentStrategy;
public void SetPaymentMethod(IPaymentStrategy strategy)
{
_paymentStrategy = strategy;
}
public void Checkout(decimal total)
{
_paymentStrategy.Pay(total);
}
}El contexto (ShoppingCart) no sabe qué método de pago está utilizando; simplemente llama a Pay() en cualquier estrategia que se haya proporcionado:
var cart = new ShoppingCart();
cart.SetPaymentMethod(new CreditCardPayment());
cart.Checkout(99.99m); // Pagado 99.99 vía Credit Card
cart.SetPaymentMethod(new PayPalPayment());
cart.Checkout(49.99m); // Pagado 49.99 vía PayPalEl patrón Strategy elimina la lógica condicional compleja. Añadir un nuevo método de pago significa crear una nueva clase que implemente IPaymentStrategy: no se necesitan cambios en ShoppingCart. Esto hace que tu código esté abierto a la extensión, pero cerrado a la modificación.
Desafío
FácilConstruyamos una calculadora de costos de envío utilizando el patrón Strategy. Los diferentes métodos de envío tienen distintos algoritmos de precios, y tu sistema debe permitir cambiar entre ellos en tiempo de ejecución sin modificar la lógica principal de procesamiento de pedidos.
Organizarás tu código en tres archivos:
ShippingStrategy.cs: Define una interfazIShippingStrategyen el espacio de nombresShippingcon un métodoCalculateCost(double weight)que devuelve undouble. Después, crea tres clases de estrategia concretas:StandardShipping: calcula el costo comoweight * 1.5ExpressShipping: calcula el costo comoweight * 3.0 + 5.0(incluye una tarifa fija de envío exprés)OvernightShipping: calcula el costo comoweight * 5.0 + 10.0(servicio premium con una tarifa base más alta)
OrderProcessor.cs: Crea una claseOrderProcessoren el mismo espacio de nombres. Esta es tu clase de contexto, que utiliza una estrategia de envío. Debe tener:- Un campo privado para almacenar el
IShippingStrategyactual - Un método
SetShippingMethod(IShippingStrategy strategy)para cambiar la estrategia de envío - Un método
ProcessOrder(double weight)que utilice la estrategia actual para calcular y devolver el costo de envío
- Un campo privado para almacenar el
Program.cs: Reúne todo creando unOrderProcessory demostrando cómo puedes intercambiar estrategias de envío en tiempo de ejecución. El mismo procesador puede calcular costos utilizando distintos algoritmos simplemente cambiando su estrategia.
Recibirás dos entradas:
- El tipo de método de envío:
standard,expressuovernight - El peso del paquete como número decimal (por ejemplo,
4.5)
Crea un OrderProcessor, establece la estrategia de envío adecuada según el tipo de entrada, después procesa el pedido e imprime el costo calculado.
Por ejemplo, si las entradas son express y 4.5, la salida debe ser:
18.5Esto demuestra el poder del patrón Strategy: tu OrderProcessor no contiene ninguna lógica de cálculo de envíos. Simplemente delega en la estrategia que se le haya asignado, lo que facilita agregar nuevos métodos de envío (como DroneDelivery) sin modificar el procesador en absoluto.
Pruébalo tú mismo
using System;
using Shipping;
class Program
{
public static void Main(string[] args)
{
// Leer la entrada
string shippingType = Console.ReadLine();
double weight = Convert.ToDouble(Console.ReadLine());
// TODO: Crea una instancia de OrderProcessor
// TODO: Según shippingType ("standard", "express" o "overnight"),
// crea la estrategia de envío adecuada y asígnala al procesador
// TODO: Procesa el pedido e imprime el costo calculado
}
}
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 OOP
Archivos externosNamespaces y directivasIntro a clases y objetosLa palabra clave 'this'Métodos y parámetrosCampos vs PropiedadesConstructoresInicializadores de objetosResumen - Calculadora simple4Herencia
Sintaxis básica de herencia (:)La palabra clave 'base'Palabras clave Virtual y OverrideClases selladas (Sealed)La clase base 'object'Resumen: Jerarquía de empleados7Características avanzadas
Sobrecarga de operadoresIndexadores (this[])Sobrescritura de ToString()Métodos de extensiónResumen - Lista personalizada10Patrones de diseño - Parte 1
Introducción a los patrones de diseñoSingleton Thread-SafePatrón FactoryPatrón Observer (Eventos)Patrón Strategy2Propiedades y miembros estáticos
Propiedades autoimplementadasPropiedades de solo lectura y escrituraCampos y métodos estáticosClases estáticasMiembros con cuerpo de expresión5Polimorfismo e Interfaces
Polimorfismo: Compilación vs. EjecuciónInterfaz vs. Clase AbstractaInterfaces MúltiplesInterfaces ExplícitasUpcasting y DowncastingRepaso: Calculadora de Figuras8Conceptos avanzados de POO
Composición sobre herenciaGenéricos (clases y métodos)Delegados y eventosAtributos y ReflexiónIDisposable y la sentencia usingFundamentos de Inyección de dependencias11Patrones de diseño, parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón Composite3Arquitectura de clases
Datos de instancia vs. estáticosPalabras clave 'readonly' y 'const'Campos de respaldoResumen - Gestor de cuentas bancarias6Encapsulamiento
Modificadores de accesoPropiedades para el encapsulamientoImplementación de ocultamiento de datosPatrones de inmutabilidadRepaso - Registros de estudiantesPractica por tu cuenta: Compilador de C# online