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.
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 abstractaFormattingStrategycon un métodoString format(String text). Luego, implementa tres estrategias concretas:UppercaseStrategy- convierte el texto a mayúsculasLowercaseStrategy- convierte el texto a minúsculasTitleCaseStrategy- 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)
TextProcessorque contenga unaFormattingStrategy. Debe tener un constructor que acepte una estrategia inicial, un métodosetStrategy()para cambiar la estrategia en tiempo de ejecución y un métodoprocess(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 unTextProcessorcomenzando con laUppercaseStrategy. Procesa el texto'hello world'e imprime el resultado. Luego cambia aLowercaseStrategy, procesa'HELLO WORLD'e imprime. Finalmente, cambia aTitleCaseStrategy, 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 FoxHoja 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 PayPalLa 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
}
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 POO
Archivos externosLibrerías e importacionesIntroducción a la POOClases vs. ObjetosLa palabra clave thisMétodosVariables de instanciaConceptos básicos de constructoresResumen - Calculadora sencilla4Seguridad de nulos
Introducción a la seguridad de nulosNulables vs No nulablesLos operadores ? y !Palabra clave Late y Null SafetyOperadores Null-awareNull Safety en clasesResumen - Sistema de perfil de usuario7Clases abstractas e interfaces
Clases abstractasMétodos abstractosInterfaces en DartInterfaces implícitasImplementar vs. ExtenderInterfaces múltiplesRepaso - Calculadora de figuras10Colecciones y Genéricos
Visión general de List, Set y MapColecciones con seguridad de tiposClases genéricasMétodos genéricosRestricciones genéricasIterable e IteratorResumen - Almacenamiento genérico2Constructores en Dart
Constructor por defectoConstructores nombradosListas de inicializaciónConstructores constantesConstructores de fábricaConstructores de redireccionamientoResumen - Constructor de formas5Encapsulamiento
Miembros públicos vs. privadosLa convención del prefijo _Privacidad a nivel de libreríaProfundidad en Getters y SettersOcultamiento de informaciónResumen - Registros de estudiantes8Mixins
Introducción a los MixinsCreación de MixinsUso de múltiples MixinsPalabra clave on en MixinsMixin vs HerenciaMixin vs InterfazResumen - Sistema Animal11Métodos especiales
Sobrescritura de toString()Sobrescritura de hashCode y ==Interfaz ComparableMétodo call()Sobrescritura de noSuchMethodResumen - Colección personalizada14Patrones de diseño Parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón ObserverPatrón Strategy3Propiedades de clase
Miembros de instancia vs. estáticosCampos Final y ConstVariables LateMétodos y campos estáticosGetters y SettersResumen - Gestor de cuentas bancarias6Herencia
Herencia básicaLa palabra clave superSobrescritura de métodosLa anotación @overrideLa palabra clave final en clasesConstructores y herenciaResumen - Jerarquía de empleados9Polimorfismo
Fundamentos del polimorfismoPolimorfismo mediante interfacesComprobación de tipos en tiempo de ejecuciónLos operadores is y asPalabra clave covariantResumen - Procesador de pagos12POO Asíncrona
Futures y async/awaitFundamentos de StreamsStream ControllersConstructores asíncronosAsincronía en métodos de claseRepaso - Data Fetcher15Patrones de diseño Parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón CompositePatrón Repository