Este patrón se suele utilizar cuando una clase controla el acceso a un recurso físico único o cuando hay datos que deben estar disponibles para todos los objetos de la aplicación.
El patrón Singleton se implementa mediante una clase con un constructor privado. Existirá un método que creará una instancia del objeto llamando al constructor sólo si todavía no existe ninguna.
Se pueden producir problemas en programas con múltiples hilos de ejecución, si dos hilos de ejecución intentan crear la instancia al mismo tiempo y ésta no existe todavía. Para que sólo uno de ellos pueda crear el objeto se puede utilizar exclusión mutua (synchronized) en el método de creación de la clase que implementa el patrón.
Habría que decir también que si existen varios cargadores de clases para la aplicación puede existir una instancia de la clase en cada uno de ellos, aunque la instancia sea única dentro de ese cargador.
Una implementación correcta del patrón Singleton en Java podría ser la siguiente, propuesta por el investigador Bill Pough:
public class Singleton {
// Private constructor prevents instantiation from other classes
private Singleton() {}
/**
* SingletonHolder is loaded on the first execution of
* Singleton.getInstance() or the first access to
* SingletonHolder.INSTANCE, not before.
*/
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
A pesar de que es ampliamente utilizado, existe una corriente contraria al uso de este patrón debido a los problemas que introduce. En esta página Scout Densmore expone los problemas que Brian Button achaca a los Singletons. A continuación, hago una adaptación de lo ahí se dice:
- Los Singletons normalmente se usan para proporcionar un punto de acceso global para un servicio. De esta forma no hace falta pasar una referencia a este servicio, lo que viene a ser como usar una variable global. Al final lo que ocurre es que las dependencias de diseño permanecen ocultas dentro del código y no son visibles al examinar las interfaces. Hace falta inspeccionar el código para entender qué objetos están usando las clases.
- Los Singletons permiten limitar la creación de los objetos. Esto es cierto, pero ahora se están mezclando dos responsabilidades diferentes en la misma clase. Una clase no debería preocuparse de si es o no un Singleton, sino que sólo debería preocuparse por sus responsabilidades de negocio. Para limitar la creación de clases se deberían crear factorías u otros objetos que limiten la creación. De esta forma, cada objeto mantendría una responsabilidad mejor definida.
- Los Singletons promueven el acoplamiento fuerte entre clases. Un acoplamiento flojo entre clases permite sustituir implementaciones alternativas de las clases colaboradoras en las pruebas. Una mejor alternativa es modificar el diseño para pasar referencias a los objetos en las clases y métodos, de forma que se reduzca el acoplamiento.
- Los Singletons mantienen el estado hasta la finalización del programa. Este estado persistente es perjudicial para las pruebas unitarias, puesto que cada prueba debe ser independiente de las demás. Si esto no se cumple, podría llevar a situaciones donde las pruebas fallen donde no deben y a que se estén ocultando errores. Esto se puede evitar pasando referencias a objetos a las clases y métodos.
Es por ello que parece un patrón de diseño a evitar dentro de lo posible.
Referencias:
Patrón Singleton en Wikipedia
Singleton Pattern — Patrones de diseño
Singletons en Java, el patrón instancia única por David Barral
Patrón Singleton en c2.com
Why Singletons are Evil por Scott Densmore
Otros patrones de diseño