El patrón Strategy
l patrón Strategy puede dar lugar a confusión por culpa de su nombre la primera vez que nos enfrentamos a él. A diferencia de otros patrones no deja muy claro cuál es su propósito, por ese motivo en mi cabeza cada vez que resuena el nombre de este patrón lo asocio con otra palabra: «comportamiento».
Muchos de vosotros y vosotras habrán comenzado el el mundo de la programación orientada a objetos aprendiendo a implementar funcionalidades mediante la sacrosanta herencia, es decir, mediante la generalización de un comportamiento. Si todos los animales hacen sonidos, crearemos una clase Animal (a poder ser abstracta) que permita la escritura del método <code>sonido()</code> en las clases hijas. Esto es lo primero que nos enseñan y hasta que no comenzamos a estudiar los patrones de diseño, creemos que todo se puede solucionar extendiendo clases e implementando interfaces. Para un martillo todo son clavos y conocer patrones de diseño te permite no apretar una tuerca a golpes.
El eterno ejemplo de los animales y sus sonidos con el que todas y todos nos hemos criado en herencia presenta algunos problemas cuando nos planteamos las siguientes dos preguntas.
-
¿Que pasa si no todos los animales ejecutan un sonido?
Podríamos sobrescribir el método sin ningún código (nada elegante ni práctico) o bien crear una interfaz que solo implementaran los animales que generasen sonidos.
Sin embargo la primera opción plantea el problema de pedazos de código inútil, y si hacemos esto en esquemas de objetos relativamente grandes, pronto nos encontraremos con una ingente cantidad de métodos inútiles en clases que no deberían tener prácticamente código, dificultando su lectura y mantenimiento.
La segunda opción parece la clásica. Yo elijo qué objetos implementan qué comportamiento, pero… ¿y el código repetido? ¿Te has planteado lo que es poner System.out.println(«Guau») en el medio centenar de clases que heredan de perro?¿Y lo que supondría modificar el comportamiento de todas ellas en un futuro?
Si eres conocedor del JDK8 podrías decir «ahora las interfaces pueden implementar códigos por defecto» como se explica aqui, pero obviemos esa posibilidad porque quiero seguir pensando que en Java la herencia múltiple no existe.
-
¿Qué pasa si quiero que un sonido cambie en tiempo de ejecución?
Querido, lector, ni siquiera el JDK8 te da solución a este problema. Necesitas implementar definitivamente el patrón Strategy.
Entremos pues en la respuesta que da el patrón Strategy al problema:
Insertar un atributo que defina el comportamiento del objeto y que sea polimórfico.
Esto se ve mucho mejor con un diagrama de clases:
La clase Perro contiene un objeto en el que confía su comportamiento (en este caso emitir un sonido).
Cuáles son las ventajas de esto?
- Un comportamiento mutable en tiempo de ejecución.
- Mejoras tanto en mantenimiento como extensibilidad. Evitamos repetir código y nos permite hilar más fino en un futuro.
Veamos un ejemplo de la facilidad con la que podemos cambiar el comportamiento de un objeto Perro del anterior diagrama.
Como veis, resulta extremadamente sencillo variar el comportamiento de una clase mediante un patrón nada difícil de implementar.
Podéis ver otro ejemplo de este patrón junto con varios otros en mi repositorio de Github.