lunes, 11 de octubre de 2010

Ejemplo Sencillo del patrón Singleton

El patrón Singleton es quizás uno de los patrones más básicos que deberíamos entender por su extendida aplicabilidad en el diseño de clases, y también porque es uno de los patrones más usados en las entrevistas para probar conocimientos en patrones de diseño.

No pretendo extenderme mucho en la descripción del patrón. La definición más simple es que provee una sola instancia de un objeto para una determinada clase. A continuación dejo un ejemplo de cómo implementar este patrón:


/**
* Simple example of the Singleton pattern.
* @author gsolano
*
*/
public class MySingleton {

   /**
   * The only instance that is going to be used.
   */
   protected static MySingleton myInstanceOfSingleton;
   protected String fooValue;

   /**
   * Gets the instance of this class.
   * @return
   */
   public static MySingleton getInstance() {
      if(myInstanceOfSingleton == null) {
         synchronized (MySingleton.class) {
            myInstanceOfSingleton = new MySingleton();
         }
      }
      return myInstanceOfSingleton;
   }

   /**
   * Private constructor to prohibit instantiation of this class.
   */
   private MySingleton() {
      fooValue ="Hello World!!";
   }

   public void setFooVaule(String value) {
      this.fooValue = value;
   }

   public void printFooValue() {
      System.out.println(fooValue);
   }

   public static void main(String[] args) {
      MySingleton mySingleton = MySingleton.getInstance();
      mySingleton.printFooValue();
      mySingleton.setFooVaule("This is Singleton!!");

      MySingleton mySecondSingleton = MySingleton.getInstance();
      // Prints the  value set in mySingleton variable.
      mySecondSingleton.printFooValue();
   }
}
Salida:



Cabe resaltar dos aspectos importantes de este ejemplo de Singleton.
  1. El constructor es declarado como privado para prohibir crear muútiples instancias de esta clase.
  2. La única puerta para acceder la clase es a través del método “getInstance” que internamente instancia una sola vez el objeto que se utilizará en todo el contexto de la aplicación, y el resto de las veces retorna una referencia a la única instancia de la clase.

2 comentarios:

  1. Creo que hay un problema con la sincronización en el método getInstance.

    La evaluación de la variable:

    [code]
    if(myInstanceOfSingleton == null) {
    ...
    }
    [/code]

    Esta fuera del bloque sinchronizado.

    Digamos que un tread t1 acaba de hacer la evaluación, pero aun no obtiene el lock (en la siguiente instruccion).
    Simultaneamente, un tread t2 hace la evaluacion y resulta false tambien.
    En ese momento threat t1 obtiene el lock sobre la clase.
    Pero ya es demasiado tarde. Cuando treat t1 suelte el lock, thread t2 lo obtendra y redefinira la instancia del singleton.

    Ahi se rompera el patron. Dos instancias del singleton habran sido creadas.

    Por lo tanto, en este caso es mejor sincronizar el metodo o todo el bloque. El problema es que la variable compartida, en este caso la instancia del singleton no esta protegida por la sincronizacion.

    En realidad la mejor es estrategia es utilizar un inicializador estático

    [code]
    protected static MySingleton myInstanceOfSingleton = new MySingleton();
    [/code]

    Y así evitas la sincronización del todo, lo que contribuye al performance. Y en este caso, no hay razón para que la instancia se cree en getInstance si se puede crear en la inicializacion estática de la case.

    Tambien hay una técnia de lazy loading llamada Initialization on demand holder idiom. (Abajo puse el link por si te interesa)

    ¿Que te parece?

    http://en.wikipedia.org/wiki/Initialization_on_demand_holder_idiom

    http://en.wikipedia.org/wiki/Singleton_pattern#Traditional_simple_way

    ResponderEliminar
  2. Desde que existen los Ioc, no he vuelto a usar el maravilloso patron :)

    ResponderEliminar