Patrón Singleton en Java, con ejemplos
El proyecto de este post los puedes descargar pulsando AQUI.
En este tutorial vamos a hablar del patrón de diseño "Singleton", que en ingeniería del software es un patrón diseñado para limitar la creación de objetos pertenecientes a una clase. El objetivo de este patrón es el de garantizar que una clase solo tenga una instancia (o ejemplar) y proporcionar un punto de acceso global a ella. Esta patrón; por ejemplo, suele ser utilizado para las conexiones a bases de datos.
Este patrón se implementa haciendo privado el constructor de la clase y creando (en la propia clase) un método que crea una instancia del objeto si este no existe. Veamos a continuación un ejemplo de este patrón en el que vamos a crear una clase llamada "SoyUnico" con un constructor que va a ser privado y un método llamado 'getSingletonInstance()' que será el encargado de crear una instancia de esta clase si no se ha creado todavía:
public class SoyUnico { private String nombre; private static SoyUnico soyUnico; // El constructor es privado, no permite que se genere un constructor por defecto. private SoyUnico(String nombre) { this.nombre = nombre; System.out.println("Mi nombre es: " + this.nombre); } public static SoyUnico getSingletonInstance(String nombre) { if (soyUnico == null){ soyUnico = new SoyUnico(nombre); } else{ System.out.println("No se puede crear el objeto "+ nombre + " porque ya existe un objeto de la clase SoyUnico"); } return soyUnico; } // metodos getter y setter }
Vemos como al llamar al método 'getSingletonInstance()' pregunta si el atributo 'soyUnico' es null o no, y si no lo es lo crea. En el caso de que ya haya sido creado no imprimirá un mensaje por pantalla diciendonos que ya existe un objeto de esa clase.
Vamos a ver como efectivamente nos crea un solo objeto, ejecutando el siguiente código en el que llamamos dos veces el método 'getSingletonInstance()':
public class Main { public static void main(String[] args) { SoyUnico ricardo = SoyUnico.getSingletonInstance("Ricardo Moya"); SoyUnico ramon = SoyUnico.getSingletonInstance("Ramón Invarato"); // ricardo y ramon son referencias a un único objeto de la clase SoyUnico System.out.println(ramon.getNombre()); System.out.println(ricardo.getNombre()); } }
Como salida a este código tenemos lo siguiente:
Mi nombre es: Ricardo Moya No se puede crear el objeto Ramón Invarato porque ya existe un objeto de la clase SoyUnico Ricardo Moya Ricardo Moya
Vemos como efectivamente cuando no hay un objeto de la clase "SoyUnico", lo crea y si lo quiere volver a crear, el método nos dice que ya hay otro creado pero le asigna la referencia al objeto, es decir que las variable 'ricardo' y 'ramon' referencian al mismo y único objeto de la clase "SoyUnico"; tal y como podemos ver al preguntar por el atributo 'nombre' de la clase.
Por último este patrón no quedaria perfectamente implementado si no controlamos la copia (o clone) de los objetos de esta clase; por ello es importante sobrescribir el método 'clone()', para que cuando se quiera clonar este objeto se lance una excepción. Este método lo sobrescribimos de la siguiente forma:
//Sobreescribimos el método clone, para que no se pueda clonar un objeto de esta clase @Override public SoyUnico clone(){ try { throw new CloneNotSupportedException(); } catch (CloneNotSupportedException ex) { System.out.println("No se puede clonar un objeto de la clase SoyUnico"); } return null; }
Veamos que pasa si intentamos clonar el objeto de la clase "SoyUnico":
public class Main { public static void main(String[] args) { SoyUnico ricardo = SoyUnico.getSingletonInstance("Ricardo Moya"); try{ SoyUnico richard = ricardo.clone(); System.out.println(richard.getNombre()); }catch (NullPointerException ex){ ex.printStackTrace(); } } }
Vemos que nos lanza una excepción y no nos permite clonar ese objeto.
Mi nombre es: Ricardo Moya No se puede clonar un objeto de la clase SoyUnico java.lang.NullPointerException at Main.Main.main(Main.java:21)
En resumen, en este tutorial se ha puesto un ejemplo muy sencillo para ver el funcionamiento del patrón "Singleton" que es un patron de diseño muy utilizado (quizas el que más), aplicable en los casos en los que solo queremos tener un único objeto de la clase que implementamos.
Hola.
El patron Singleton está desaconsejado.
Puedes ver alguno de los argumentos aqui
https://medium.com/@mcsee/singleton-el-patr%C3%B3n-del-mal-f3fdab0e16a2
Gracias Maximiliano.
Correcto, es un patrón desaconsejado, que no prohibido. Siempre recomendamos utilizar otros patrones o soluciones alternativas al código para evitar utilizar Singleton por todos los argumentos que se mencionan.
Sin embargo, en ocasiones no queda otra más que utilizarlo para compartir estados de memoria entre diferentes puntos del código en ciertas arquitecturas por la complicación de pasar estados complejos de una manera rápida y eficaz; por ejemplo, para pasar un objeto complejo en Android, compartir estados entre Activities o Fragments se hace complicado sin un patrón de este tipo (una solución alternativa es inicializar el objeto en cada clase, si el objeto realiza mucho cálculo supone una penalización muy grande tanto en tiempo de proceso como en batería del móvil para algo que ya se tenía calculado y que se puede pasar con un patrón Singleton).
Me queda mi duda porque si hago:
public static void main(String[] args) {
SoyUnico ricardo = SoyUnico.getSingletonInstance(«Ricardo Moya»);
SoyUnico ramon = SoyUnico.getSingletonInstance(«Ramon Miers»);
SoyUnico dario = new SoyUnico(«dario»);
System.out.println(ramon.getNombre());
System.out.println(ricardo.getNombre());
System.out.println(dario.getNombre());
Si lo hago asi puedo instanciar un objeto sin necesidad de usar el metodo getSingleton osea que podria seguir instanciando….
Buenas Dardo.
Para prevenir las instancias de una clase se puede hacer privado el constructor.
De cualquier manera, la gracia de usar «getSingletonInstance» es obtener la instancia que previamente existe o crearla si no existe. Si se usa el constructor, siempre se crearía una nueva instancia.
Hola, gracias por la explicación, fue la más clara de tantos tutoriales que he visto, sigan así!!!
CUántos abremos aprendido java con vosotros!??!
Gracias!!!
De nada Alberto. Nos alegra saber que los artículos ayudan 😉
Un saludo.
Excelente explicación. Muchas gracias
Muchas gracias me ayudo mucho y esta muy bien explicado
Muuuuchisimas gracias me has salvado
Muy clara la explicación y útil, mil gracias.
salame
si mando a llamar el método getSingletonInstance, para después mandar los setters con los valores esto no tendría ningún problema, cierto?
No deberías tener ningún problema, pues con getSingletonInstance te devuelve el puntero al objeto entero. te podría dar problemas si devuelve Null, que no debiera.
¡Explicación simple! Me ha sido útil. Muchas gracias.
Es aconsejable utilizar el patrón singleton para un sistema web?
creo que podría generar bloqueos si solo se usa una conexión.
El patrón Singleton es muy útil en ciertos casos, pero sí que pudiera haber problemas con la multitaréa (si utilizas por ejemplo Spring), siempre puedes poner syncronized para evitar problemas pero esperas vas a tener. Dependerá de lo que necesites.
Asi como el clone no faltaria algo para el tema de multiples hilos accediendo?
Gracias por la explicacion!
Hola! Y feliz Navidad!
Entonces, en realidad si podríamos clonarlo pero estaría conceptualmente mal, no??
Saludos
Agustín
Hola Agustín.
La idea del patron singleton es que solo haya un objeto de esa clase. Si lo clonamos obviamente habrá 2 o más objetos con lo cual no tendría sentido utilizar este patrón. No es que conceptualmente este mal, pero haciendo un clone no estarías utilizando este patrón. Normalmente cuando he utilizado este patrón no he implementado la excepción del clone ya que asumo que estoy utilizando ese patrón y no voy a clonar ese objeto, pero lo pongo en este tutorial por si acaso.
Saludos