¿Cómo funciona el patrón de estrategia?

¿Cómo funciona, para qué se usa y cuándo se debe usar?

Vamos a explicar el patrón de estrategia de la manera más fácil:

Tienes una clase Car() con un método run() , así que la usas de esta manera en un pseudo idioma:

 mycar = new Car() mycar.run() 

Ahora, es posible que desee cambiar el comportamiento de run() sobre la marcha, mientras el progtwig se está ejecutando. Por ejemplo, es posible que desee simular una falla del motor o el uso de un botón “impulsar” en un videojuego.

Hay varias maneras de hacer esta simulación: usar declaraciones condicionales y una variable de indicador es de una sola manera. El patrón de estrategia es otro: delega el comportamiento del método run() a una subclase:

 Class Car() { this.motor = new Motor(this) // passing "this" is important for the motor so it knows what it is running method run() { this.motor.run() } method changeMotor(motor) { this.motor = motor } } 

Si desea cambiar el comportamiento del automóvil, simplemente puede cambiar el motor. (Más fácil en un progtwig que en la vida real, ¿verdad? ;-))

Es muy útil si tienes muchos estados complejos: puedes cambiarlos y mantenerlos mucho más fácilmente.

Problema

El patrón de estrategia se utiliza para resolver problemas que podrían (o se prevé que podrían) implementarse o resolverse mediante diferentes estrategias y que poseen una interfaz claramente definida para tales casos. Cada estrategia es perfectamente válida por sí misma y algunas de las estrategias son preferibles en ciertas situaciones que permiten a la aplicación alternar entre ellas durante el tiempo de ejecución.

Ejemplo de código

 namespace StrategyPatterns { // Interface definition for a Sort algorithm public interface ISort { void Sort(List list) } // QuickSort implementation public class CQuickSorter : ISort { void Sort(List list) { // Here will be the actual implementation } } // BubbleSort implementation public class CBubbleSort : ISort { void Sort(List list) { // The actual implementation of the sort } } // MergeSort implementation public class CMergeSort : ISort { void Sort(List list) { // Again the real implementation comes here } } public class Context { private ISort sorter; public Context(ISort sorter) { // We pass to the context the strategy to use this.sorter = sorter; } public ISort Sorter { get{return sorter;) } } public class MainClass { static void Main() { List myList = new List(); myList.Add("Hello world"); myList.Add("Another item"); myList.Add("Item item"); Context cn = new Context(new CQuickSorter()); // Sort using the QuickSort strategy cn.Sorter.Sort(myList); myList.Add("This one goes for the mergesort"); cn = new Context(new CMergeSort()); // Sort using the merge sort strategy cn.Sorter.Sort(myList); } } } 
  • ¿Qué es una estrategia? Una estrategia es un plan de acción diseñado para lograr un objective específico;
  • “Defina una familia de algoritmos, encapsule cada uno y haga que sean intercambiables. La estrategia permite que el algoritmo varíe independientemente de los clientes que lo usan. “(Gang of Four);
  • Especifica un conjunto de clases, cada una de las cuales representa un comportamiento potencial. Cambiar entre esas clases cambia el comportamiento de la aplicación. (la estrategia);
  • Este comportamiento se puede seleccionar en tiempo de ejecución (usando polymorphism) o tiempo de diseño;
  • Capture la abstracción en una interfaz, entierre los detalles de implementación en clases derivadas;

enter image description here

  • Una alternativa a la estrategia es cambiar el comportamiento de la aplicación mediante el uso de la lógica condicional. (MALO);
  • Al usar este patrón, es más fácil agregar o eliminar un comportamiento específico, sin tener que recodificar ni volver a probar, todas o partes de la aplicación;

  • Buenos usos:

    • Cuando tenemos un conjunto de algoritmos similares y su necesidad de cambiar entre ellos en diferentes partes de la aplicación. Con Strategy Pattern es posible evitar ifs y facilitar el mantenimiento;
    • Cuando queremos agregar nuevos métodos a la superclase que no necesariamente tienen sentido para cada subclase. En lugar de utilizar una interfaz de forma tradicional, al agregar el nuevo método, usamos una variable de instancia que es una subclase de la nueva interfaz de funcionalidad. Esto se conoce como Composición: en lugar de heredar una habilidad a través de la herencia, la clase se compone con Objetos con la capacidad correcta;

Directamente del artículo de Strategy Pattern Wikipedia :

El patrón de estrategia es útil para situaciones en las que es necesario intercambiar dinámicamente los algoritmos utilizados en una aplicación. El patrón de estrategia pretende proporcionar un medio para definir una familia de algoritmos, encapsular cada uno como un objeto y hacerlos intercambiables. El patrón de estrategia permite que los algoritmos varíen independientemente de los clientes que los usan.

Un patrón estrechamente relacionado es el patrón Delegado; en ambos casos, parte del trabajo se pasa a algún otro componente. Si entiendo correctamente, la diferencia entre estos patrones es esta (y por favor corrígeme si estoy equivocado):

  • En el patrón Delegado , el delegado es instanciado por la clase adjunta (delegando); esto permite la reutilización del código por composición en lugar de herencia. La clase adjunta puede conocer el tipo concreto del delegado, por ejemplo, si invoca su propio constructor (en lugar de utilizar una fábrica).

  • En el patrón de Estrategia , el componente que ejecuta la estrategia es una dependencia provista al componente de inclusión (uso) a través de su constructor o instalador (según su religión). El componente que usa es totalmente inconsciente de qué estrategia está en uso; la estrategia siempre se invoca a través de una interfaz.

Alguien sabe alguna otra diferencia?

Para agregar a las respuestas ya magníficas: El patrón de estrategia tiene una gran similitud con pasar una función (o funciones) a otra función. En la estrategia, esto se hace envolviendo dicha función en un objeto seguido de pasar el objeto. Algunos idiomas pueden pasar funciones directamente, por lo que no necesitan el patrón en absoluto. Pero otros lenguajes no pueden pasar funciones, pero pueden pasar objetos; el patrón luego se aplica.

Especialmente en lenguajes similares a Java, encontrará que el zoológico de tipo de letra del lenguaje es bastante pequeño y que su única forma de extenderlo es mediante la creación de objetos. Por lo tanto, la mayoría de las soluciones a los problemas es crear un patrón; una forma de componer objetos para lograr un objective específico. Los idiomas con zoológicos de tipo más rico a menudo tienen formas más simples de abordar los problemas, pero los tipos más ricos también significan que debe dedicar más tiempo a aprender el sistema de tipos. Los idiomas con disciplina de tipeo dynamic a menudo también se vuelven astutos alrededor del problema.