Menu
Coddy logo textTech

Patrón Strategy

Parte de la sección Programación Orientada a Objetos del Journey de Dart de Coddy — lección 93 de 110.

El patrón Strategy te permite definir una familia de algoritmos, encapsular cada uno en su propia clase y hacerlos intercambiables en tiempo de ejecución. En lugar de codificar el comportamiento de forma rígida en una clase, inyectas el comportamiento a través de una interfaz, lo que te permite intercambiar estrategias sin modificar la clase que las utiliza.

Considere un sistema de pago en el que desee admitir diferentes métodos de pago. En lugar de utilizar condicionales para manejar cada método, se define una interfaz de estrategia y se crean implementaciones concretas:

abstract class PaymentStrategy {
  void pay(double amount);
}

class CreditCardPayment implements PaymentStrategy {
  @override
  void pay(double amount) => print('Paid \$amount with Credit Card');
}

class PayPalPayment implements PaymentStrategy {
  @override
  void pay(double amount) => print('Paid \$amount with PayPal');
}

class ShoppingCart {
  PaymentStrategy _paymentStrategy;

  ShoppingCart(this._paymentStrategy);

  void setPaymentStrategy(PaymentStrategy strategy) {
    _paymentStrategy = strategy;
  }

  void checkout(double total) {
    _paymentStrategy.pay(total);
  }
}

void main() {
  var cart = ShoppingCart(CreditCardPayment());
  cart.checkout(99.99);  // Paid $99.99 with Credit Card

  cart.setPaymentStrategy(PayPalPayment());
  cart.checkout(49.99);  // Paid $49.99 with PayPal
}

El ShoppingCart no sabe ni le importa cómo funciona el pago; simplemente delega en cualquier estrategia que esté configurada actualmente. Puedes añadir nuevos métodos de pago creando nuevas clases de estrategia sin tocar el código del carrito. Este patrón es ideal cuando tienes múltiples formas de realizar una acción y quieres seleccionar o cambiar entre ellas dinámicamente.

challenge icon

Desafío

Fácil

¡Vamos a construir un sistema de formateo de texto utilizando el patrón Strategy! Crearás un sistema donde se pueden aplicar diferentes estrategias de formateo al texto, permitiendo a los usuarios cambiar entre formatos dinámicamente sin modificar el procesador de texto principal.

Organizarás tu código en dos archivos:

  • formatting_strategies.dart: Crea tu sistema de estrategias aquí. Define una clase abstracta FormattingStrategy con un método String format(String text). Luego, implementa tres estrategias concretas:
    • UppercaseStrategy - convierte el texto a mayúsculas
    • LowercaseStrategy - convierte el texto a minúsculas
    • TitleCaseStrategy - pone en mayúscula la primera letra de cada palabra (divide por espacios, pone en mayúscula el primer carácter de cada palabra y el resto en minúsculas)
    También crea una clase TextProcessor que contenga una FormattingStrategy. Debe tener un constructor que acepte una estrategia inicial, un método setStrategy() para cambiar la estrategia en tiempo de ejecución y un método process(String text) que aplique la estrategia actual y devuelva el resultado formateado.
  • main.dart: Importa tus estrategias de formateo y demuestra cómo el patrón Strategy permite el cambio dinámico de comportamiento. Crea un TextProcessor comenzando con la UppercaseStrategy. Procesa el texto 'hello world' e imprime el resultado. Luego cambia a LowercaseStrategy, procesa 'HELLO WORLD' e imprime. Finalmente, cambia a TitleCaseStrategy, procesa 'the quick brown fox' e imprime el resultado.

Esto demuestra el poder del patrón Strategy: tu TextProcessor no necesita conocer los detalles de cómo funciona el formateo. Simplemente delega en cualquier estrategia que esté configurada actualmente, lo que facilita la adición de nuevas opciones de formateo sin modificar el procesador.

Resultado esperado:

HELLO WORLD
hello world
The Quick Brown Fox

Hoja de referencia

El patrón Strategy define una familia de algoritmos, encapsula cada uno en su propia clase y los hace intercambiables en tiempo de ejecución. En lugar de codificar el comportamiento de forma rígida, se inyecta a través de una interfaz.

Define una interfaz de estrategia:

abstract class PaymentStrategy {
  void pay(double amount);
}

Crea implementaciones de estrategia concretas:

class CreditCardPayment implements PaymentStrategy {
  @override
  void pay(double amount) => print('Paid \$amount with Credit Card');
}

class PayPalPayment implements PaymentStrategy {
  @override
  void pay(double amount) => print('Paid \$amount with PayPal');
}

Crea una clase de contexto que utilice la estrategia:

class ShoppingCart {
  PaymentStrategy _paymentStrategy;

  ShoppingCart(this._paymentStrategy);

  void setPaymentStrategy(PaymentStrategy strategy) {
    _paymentStrategy = strategy;
  }

  void checkout(double total) {
    _paymentStrategy.pay(total);
  }
}

Utiliza el patrón inyectando y cambiando estrategias:

var cart = ShoppingCart(CreditCardPayment());
cart.checkout(99.99);  // Pagado $99.99 con Tarjeta de Crédito

cart.setPaymentStrategy(PayPalPayment());
cart.checkout(49.99);  // Pagado $49.99 con PayPal

La clase de contexto delega el comportamiento a la estrategia sin conocer los detalles de la implementación. Añade nuevas estrategias creando nuevas clases sin modificar el código existente.

Pruébalo tú mismo

import 'formatting_strategies.dart';

void main() {
  // TODO: Crear un TextProcessor comenzando con UppercaseStrategy
  
  // TODO: Procesar el texto 'hello world' e imprimir el resultado
  
  // TODO: Cambiar a LowercaseStrategy
  
  // TODO: Procesar 'HELLO WORLD' e imprimir el resultado
  
  // TODO: Cambiar a TitleCaseStrategy
  
  // TODO: Procesar 'the quick brown fox' e imprimir el resultado
}
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