Patrón Estrategia (Strategy Pattern)
Introducción
El patrón estrategia (Strategy Pattern) es un patrón de tipo comportamental (behavioral pattern), es decir, se centra en definir la forma en la que se produce el intercambio de mensajes entre distintos componentes. Básicamente, su propósito es mantener un conjunto de algoritmos (estrategias) de entre los cuales el objeto cliente puede elegir aquel que le conviene e intercambiarlo dinámicamente según sus necesidades.
Algunos ejemplos en los que puede ser útil:
Funcionalidad de exportar que permite elegir distintos formatos: PDF, CSV, XML, JSON…
Manejar las opciones de visualización de un texto: alinear izquierda, centrar, alinear derecha, justificar…
Calcular la ruta en un sistema GPS basándonos en distintos vehículos: a pie, en bici, en coche…
Mover una pieza en juego tipo ajedrez: peón, torre, caballo, alfil, dama, rey…
Actores
Hay 4 actores diferenciados en este patrón:
Cliente
Es quien solicita la acción, también es quien fija la estrategia a utilizar. No forma parte del core de la lógica, solo interactúa con el contexto.
Contexto
Es la clase que alberga la información necesaria para ejecutar las estrategias y también la clase que hace uso de ellas. Al contexto debemos de informarle de la estrategia a utilizar mediante un setter u otro mecanismo.
Interfaz de la estrategia
Es el contrato que debe de cumplir cada estrategia que queramos implementar. Este contrato permite al contexto conocer y estandarizar el uso de las estrategias, no teniendo que conocer la implementación final de cada una para tener que ejecutarlas.
Hay una forma alternativa al uso de la Interfaz de la Estrategia, que es simplemente pasarle el contexto a las estrategias concretas. En ese caso obviamente no sería necesario definir una interfaz.
Interfaz concreta
Es una implementación del contrato de la interfaz, si pensamos en alguno de los casos anteriores de ejemplo, es quien define cómo se mueve un peón, quién sabe cómo se calcula una ruta en bici, quién sabe cómo formatear un texto centrado o cómo formatear un PDF a exportar.
Motivación
Viendo en perspectiva podemos pensar que se trata de un patrón innecesariamente complicado, que podemos solucionar el mismo problema con una serie de if-else o con un switch dentro de lo que aquí llamamos contexto.
Obviamente, hay mil formas de solucionar un problema, pero cada una tiene sus pros y sus contras.
Ventajas
Entre las ventajas a destacar del patrón estrategia:
Respeta el Principio Open/Closed (OCP) y el Principio de única responsabilidad (SRP): Incluir el código de los algoritmos en los clientes hace que estos sean demasiado grandes y complicados de mantener y/o extender. Con el patrón estrategia se respeta el principio OCP, haciendo que el contexto sea abierto a extensión y cerrado a modificación. Por otra parte, las estrategias y el contexto solo tienen una razón de cambio.
Ahorro en memoria: El cliente no va a necesitar todas las estrategias en todo momento, de modo que no necesitamos cargar un objeto con toda esa lógica dentro cada vez que queramos usarlo.
Reutilización. La misma estrategia puede usarse en diferentes contextos, evitando la duplicidad del código.
Inconvenientes
Algunos puntos flacos de este patrón:
Si solo tenemos un par de estrategias a implementar, puede ser un poco exagerado o excesivo implementar este patrón, ya que complicamos en exceso la casuística.
El cliente debe de conocer las diferencias entre las estrategias.
Similitud con otros patrones
Es posible que este patrón nos recuerde a otros que ya hemos comentado en anteriores posts, como el Patrón Command, el Patrón Adapter o el Patrón Template Method.
Command
Se parecen porque ambos parametrizan un objeto con una acción definida, sin embargo tienen fines diferentes:
Con Command conviertes una operación en un objeto, esto te permite aplazar la ejecución del mismo, encolarla, guardar un histórico de ejecución o incluso enviarlos a servicios remotos como en sistemas RPC.
Strategy en cambio te permite ejecutar distintas formas de una misma acción sobre un mismo contexto.
Adapter
Tiene un parecido estructural, ya que ambos se basan en distintas implementaciones de una misma interfaz. Sin embargo, solucionan problemas diferentes.
Template Method
La diferencia con Template Method es que este e basa en herencia, a diferencia de Strategy que se basa en composición. Por otra parte Template Method funciona a nivel clase, por lo que es estático, mientras que Strategy lo hace a nivel objeto y nos permite cambiar su comportamiento en tiempo de ejecución.
Ejemplo de implementación
Aquí se puede ver una pequeña implementación del patrón Strategy:
Referencias
Refactoring to patterns de Joshua Kerievsky
Top comments (0)