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.

Comparte esta entrada en:
Safe Creative #1401310112503
Patrón Singleton en Java, con ejemplos por "www.jarroba.com" esta bajo una licencia Creative Commons
Reconocimiento-NoComercial-CompartirIgual 3.0 Unported License.
Creado a partir de la obra en www.jarroba.com

21 thoughts on “Patrón Singleton en Java, con ejemplos”

    1. 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).

      1. 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….

        1. 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.

  1. 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?

    1. 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.

  2. Es aconsejable utilizar el patrón singleton para un sistema web?

    creo que podría generar bloqueos si solo se usa una conexión.

    1. 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.

  3. Hola! Y feliz Navidad!

    Entonces, en realidad si podríamos clonarlo pero estaría conceptualmente mal, no??

     

    Saludos

    Agustín

    1. 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

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.

Uso de cookies

Este sitio web utiliza cookies para que usted tenga la mejor experiencia de usuario. Si continúa navegando está dando su consentimiento para la aceptación de las mencionadas cookies y la aceptación de nuestra política de cookies

ACEPTAR
Aviso de cookies