En la edición anterior hicimos foco en el patrón estructural DECORATOR. En esta entrega vamos a indagar en el patrón creacional SINGLETON. Allá vamos!
Como comenté en el artículo introductorio a los patrones de diseño (acá el link), un patrón creacional es aquel que facilita la creación de objetos encapsulando el proceso.
También vimos que hay 4 elementos que definen un patrón. Así que vamos a utilizarlos para exponer el patrón SINGLETON.
Nombre: SINGLETON
Problema: Costosa creación de una clase que se utiliza en todo el flujo de ejecución.
Solución: Crear una sola instancia que sea de acceso global.
Consecuencias:
- Ventajas: Asegurar que hay sólo una instancia de una clase.
- Desventajas: En sí, este patrón no tiene desventajas. Pero está relacionado a un anti patrón en el cual se hace uso excesivo del patrón. Añadiendo restricciones innecesarias cuando no se requiere que haya una sola instancia.
El diagrama de clases es bastante sencillo y quedaría de la siguiente manera:
La clase es la que se encarga de manejar y proveer la instancia. Así como de asegurar que sólo hay una.
Es uno de los patrones de diseño más sencillos. La clase debe cumplir dos reglas: Debe tener sólo una instancia y debe ser de acceso global.
También se destaca por tener dos tipos de inicializaciones. Una temprana, en la cual tenemos una instancia desde que comienza el flujo de ejecución, y otra lazy, en la cual se crea la primera vez que se requiere la instancia.
Conclusión
Es un patrón de diseño simple y fácil de implementar, no trae inconvenientes si se contemplan los posibles casos en los cuáles puede que se cree más de una instancia.
Hay que tener en cuenta si nuestra aplicación puede tener diversos hilos de ejecución ya que si tenemos una inicialización lazy y las dos van a buscar una instancia a la vez, puede que se terminen generando dos instancias de la clase si no lo tenemos en cuenta.
Otro caso que puede hacer que terminemos con más de una instancia es que se clone la instancia. Esto se puede evitar fácilmente sobrescribiendo los métodos de clonación y lanzando una excepción.
También, hay que tener en cuenta si nuestra clase pasa por un proceso de deserialización ya que si no tomamos las medidas necesarias se va a generar una nueva instancia. En ese caso deberíamos implementar la interfaz de serialización y su método readResolve() para devolver nuestra instancia.
En el caso de que la clase posea un estado hay que tener en cuenta que al ser una sola instancia al modificarse en una parte del flujo o en otro thread, se va a modificar para todos.
Con esto último cierro el artículo y espero que les haya servido!