Patrón Singleton en Java

El patrón singleton es de los patrones de diseño más sencillos tanto porque esta muy claro cual es su objetivo como por lo sencilla que resulta su implementación.

Mediante este patrón podemos hacer que únicamente haya una sola instancia de una clase en nuestra aplicación de forma similar a una variable global pero de una forma más elegante.

Este patrón es ampliamente utilizado ya que hay multitud de ocasiones en las que se necesita que necesita compartir alguna información en la aplicación, tener un único punto de acceso a un recurso o cualquier situación en la que se necesite tener un solo objeto de una clase.

Una vez visto un poco para que se usa el patrón singleton vamos a ver como se realiza su implementación en java:


public class ClaseSingleton {
    private static ClaseSingleton instanciaUnica = new ClaseSingleton();
 
    private ClaseSingleton() {}
 
    public static ClaseSingleton getInstance() {
        return instanciaUnica;
    }
}   

El código habla por si solo pero vamos a explicarlo línea por línea:

  • El primer paso es crear en la propia clase un objeto de la propia clase, que será la única instancia de nuestra clase.
  • Para impedir que se puedan crear nuevas instancias de nuestra clase tenemos que declarar el constructor de nuestra clase como privado para evitar que se pueden crear objetos desde fuera desde nuestra clase.
  • Finalmente el ultimo paso es declarar un método publico para obtener el objeto que hemos creado en un primer momento y de esta forma dotar de una forma de acceso a la única instancia de nuestra clase.

Así de sencillo es implementar el patrón singleton en java, hay algunas pequeñas variantes posibles como inicializar el objeto dentro de un método en lugar de hacerlo en la declaración pero si se hace de este modo no hay que olvidar que a la hora de comprobar si ya hay alguna instancia creada hay que hacerlo de forma sincronizada porque si se da el caso de que se hacen varias llamadas simultaneas cuando aun no se ha creado ninguna instancia puede suceder que se cree más de una instancia con lo que habremos fallado en nuestro intento de que solo haya una instancia de nuestra clase.

A continuación pongo el ejemplo de como no se debe de hacer porque podrían crearse varias instancias y aunque ciertamente no es que este mal, si que es cierto que solo es valido para aplicaciones en las que hay un solo hilo de ejecución, pues aunque sea improbable que se creen varias instancias puesto que solo podría suceder en el momento en el que aun no hay ninguna y que además en ese momento se produjesen varias llamadas al método getInstance() siempre se deben tener en cuenta estos casos para evitar fallos en nuestras aplicaciones.


public class ClaseSingleton {
    private static ClaseSingleton instanciaUnica;
 
    private ClaseSingleton() {}
 
    public static ClaseSingleton getInstance() {
        if (instanciaUnica == null) {
            instanciaUnica = new ClaseSingleton();
        }
     
        return instanciaUnica;
    }
}   

La forma «correcta» para el código anterior sería la siguiente:


public class ClaseSingleton {
    private static ClaseSingleton instanciaUnica;
 
    private ClaseSingleton() {}

    private synchronized static void createInstance() {
        if (instanciaUnica == null) { 
            instanciaUnica = new ClaseSingleton();
        }
    }
 
    public static ClaseSingleton getInstance() {
        createInstance();

        return instanciaUnica;
    }
}   

La esencia es la misma que la del código anterior pero la comprobación de si ya se ha creado un objeto de nuestra clase se hace dentro de un método sincronizado por lo que nos aseguramos de que solo pueda haber una instancia de nuestra clase.

Para terminar voy a poner un ejemplo de uso del patrón singleton un poco peculiar pero que creo que permite ver perfectamente como funciona este patrón y la diferencia entre utilizarlo y no hacerlo.

El ejemplo que propongo es hacer una clase que simule un reloj, es decir, que nos vaya dando la hora cada segundo (para hacerlo sencillo simplemente haremos una aplicación de consola en la que imprimiremos la hora por pantalla cada segundo) y el reloj se va a crear usando el singleton porque no tiene sentido tener varias instancias distintas porque todas nos darían la misma hora y además al hacerlo como una aplicación de consola si creamos más de un objeto de la clase reloj se nos imprimiría cada hora tantas veces como instancias de la clase reloj hubiésemos creado previamente.


package com.poi.singleton;

import java.util.Date;

public class Reloj extends Thread {

    private static Reloj reloj;

    /**
     * Constructor privado por que se usa el patron Singleton
     */

    private Reloj() {
    }

    // /**
    // * Constructor publico para probar sin singleton
    // */

    // public Reloj() {
    // this.start();
    // }

    /**
     * Inicializa una sola vez el reloj
     */

    private synchronized static void createInstance() {
        if (reloj == null) {
            reloj = new Reloj();
            reloj.start();
        }
    }

    /**
     * Obtiene la unica instancia del gestor de geozonas
     * 
     * @return gestorGeozonas
     */

    public static Reloj getInstancia() {
        createInstance();

        return reloj;
    }

    /**
     * Imprime por pantalla la hora cada segundo
     */

    @Override
    public void run() {
        while (true) {

            Date hora = new Date(System.currentTimeMillis());
            System.out.println(hora);

            try {
                sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }

    }

}

El código es sencillo, pero como la clase Reloj además de implementar el patrón Singleton también crea un hilo de ejecución que se encargue de imprimir la hora cada segundo sin bloquear el hilo principal por si quisiésemos hacer alguna otra tarea mientras tanto hay que inicializar el thread en el momento en el que se inicializa el reloj dentro del método createInstance() porque el hilo solo hay que inicializarlo una sola vez.

Y para crear un reloj en lugar de hacer un new Reloj(), que no podemos hacer porque hemos declarado el constructor como privado tenemos que hacer uso del método getInstancia() y para ver que aunque intentemos crear varias instancias solo se crea intentamos crear 3 relojes.


package com.poi.singleton;

public class Main {

    public static void main(String[] args) {
        
        // 3 relojes usando el patrón singleton    
        Reloj r = Reloj.getInstancia();
        Reloj r2 = Reloj.getInstancia();
        Reloj r3 = Reloj.getInstancia();
        
//      // 3 relojes sin usar el patrón singleton      
//      Reloj r4 = new Reloj();
//      Reloj r5 = new Reloj();
//      Reloj r6 = new Reloj();
    }

}

Tanto en la clase Main como en la clase Reloj hay algunas líneas comentadas por si quieres probar que sucede si no usamos el patrón singleton y ver las diferencias.

Finalmente, si quieres este ultimo proyecto de ejemplo puedes descargar el código completo desde aquí.

3 Comments

  1. Marcelo 6 febrero, 2015 Reply
    • Iván Salas 6 febrero, 2015 Reply
  2. sfasdf 3 enero, 2018 Reply

Leave a Reply