Cómo implementar el Strategy Pattern con Enums en Java

Ventajas y desventajas, y algún que otro insight respecto a esta práctica.

Read this article in English here

Primero lo primero. ¿Qué es el Strategy Pattern?

El Strategy Pattern es uno de los 23 patrones de diseño presentados por el grupo Gang of Four (GoF), y se encuentra dentro de la categoría de patrones de comportamiento.

“El Strategy Pattern define una familia de algoritmos y los hace intercambiables”

Este patrón de diseño nos permite aplicar un concepto importante en la programación orientada a objetos que es favorecer la composición por sobre la herencia, lo que se traduce a poder modificar el comportamiento de un objeto en tiempo de ejecución.

Hecha esta pequeña introducción sobre el patrón de diseño Strategy, veamos un poco de código.

Supongamos (con mucha imaginación) que estamos trabajando sobre un motor de búsqueda. Este motor de búsqueda trabaja sobre una fuente de datos, buscando un valor en particular.

Los algoritmos más comunes para realizar una búsqueda podríamos decir que son la búsqueda lineal y la búsqueda binaria. Esta última tiene la particularidad de ser (en promedio) mucho más rápida que la lineal a cambio de exigir un pre-requisito: que la fuente de datos se encuentre ordenada.

Entonces, dentro de la terminología del Strategy Pattern, tenemos una familia de algoritmos (algoritmos de búsqueda) y queremos poder intercambiarlos dentro de nuestro motor de búsqueda.

Como se puede ver, el motor de búsqueda, luego de ordenar la fuente de datos, intercambia su estrategia de búsqueda a la búsqueda binaria, ya que teniendo la lista ordenada, este es el mejor algoritmo de búsqueda.

Para todo esto tenemos apenas tres clases y una interfaz. Bastante bien, ¿no?.

Enum

El mismo resultado se puede alcanzar usando una implementación basada en los Enum de Java.

El comportamiento es el mismo, pero en lugar de utilizar una interfaz y múltiples implementaciones, utilizamos sólo un Enum.

Ventajas de la implementación con Enum

En primer lugar, la cantidad de clases se reduce sin generar un acoplamiento absurdo, por el contrario, todo el código del Enum es cohesivo y responde a la misma necesidad: realizar una búsqueda.

La responsabilidad de la búsqueda queda bien definida en un mismo Enum.

En segundo lugar, los Enum son singletons, es decir, existe una y solo una instancia de cada implementación.
“¿Qué pasó acá? ¿2 patrones en 1?” Si, ya se que muchos desarrolladores consideran el Singleton como un “Antipatrón”, y esa será otra discusión, pero esta cuestión nos ahorra la complejidad de tener que instanciar objetos y terminar usando otros patrones tales como Builder y Factory.

Los Enum son singletons: No tenemos que instanciarlos ni molestarnos en implementar factories o builders.

En tercer lugar, nuestro Strategy gana la capacidad de persistirse. Las bases de datos relacionales suelen soportar valores enumerados, y con un buen framework de ORM (Object Relational Mapping) podemos muy fácilmente persistir nuestro SearchEngine y su estado (su fuente de datos y su estrategia, por ejemplo).
Esta idea llevada a otros dominios (usuarios con roles y perfiles, vehículos con distintos equipamientos, etcétera) hace que tengamos control sobre los algoritmos que se utilizan en tiempo de ejecución no sólo desde el código sino también desde la base de datos.
Se puede pensar, en cierta manera, que estaríamos persistiendo algoritmos.

Si tuviéramos que persistir los SearchEngine en la implementación anterior a los Enums utilizando un framework de ORM, posiblemente necesitemos algún campo serializable que determine qué Strategy está configurado en un instante determinado, y utilizar este campo al recuperar los datos de la base para determinar, luego de algunas comparaciones y creaciones de objetos, qué Strategy asignar a nuestra instancia en tiempo de ejecución.
Con la implementación basada en Enums, este campo serializable es el Enum, y las comparaciones y creaciones de objetos no son necesarias.

Desventajas de la implementación con Enum

No todo lo que brilla es oro, y también hay desventajas.

Para empezar, como mencioné antes, los Enum son singletons, es decir, instancias únicas que se comparten en toda la aplicación. Esto implica que los Enums deberían ser stateless (objetos sin estado), al menos en la mayoría de las aplicaciones que vayan a soportar algún tipo de concurrencia.
Si bien no es muy común que un Strategy maneje un estado, si nuestra implementación requiere lidiar con este, los Enum no son el camino a seguir.

Los Strategy que implementemos deben ser “stateless”.

Otro tema a tener en cuenta es la cantidad de implementaciones del Strategy que tengamos. No es lo mismo tener dos, tres o cuatro algoritmos diferentes de la misma familia, que diez o más. En ese caso, tendremos que decidir si queremos tener un Enum con cientos (o miles) de líneas de código o si preferimos repartir el código en distintas clases que implementen una interfaz.

Por último, pero no menos importante, los Enum no soportan herencia de clases (aunque sí de interfaces). Si quisiéramos tener algo como RecursiveBinarySearchStrategy e IterativeBinarySearchStrategy, ambas subclases de BinarySearchStrategy, necesitamos algo más complejo que un simple Enum.

Conclusiones

La idea era mostrar una alternativa a la forma clásica de pensar el Strategy Pattern (múltiples clases implementando una interfaz) que reduce mucho el código “boilerplate” (ese código que odiamos escribir pero no nos queda otra) y nos suma una forma diferente de ver los Enum, ya no sólo como valores enumerados, sino como algoritmos enumerados.
También, sumar la idea de persistir algoritmos de una forma simple y transparente.

Usar o no Enums para implementar código es una decisión que cada desarrollador sabrá tomar según su criterio y el costo/beneficio que prefiera, pero tal vez con esta breve lectura, tenga alguna herramienta extra.

Written by

Just another Software Engineer who loves programming, reading and teaching

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store