Temas etiquetados como: ‘Patrones Java’

Definir una Singleton en Java mediante un enum

15 Febrero, 2010

El otro día leyendo el libro de Effective Java de Joshua Bloch, me encontré con una nueva forma de declarar el patrón de diseño Singleton.

Una singleton es simplemente una clase que es instanciada exactamente una vez. Antes de Java 1.5 había dos formas de implementar una singleton, ambas se basan en un contructor privado y en proporcionar un miembro público y estático que da acceso a una sola instancia. A partir de Java 1.5 aparece una tercera opción para implementar una singleton, simplemente mediante un tipo enumerado con un único elemento:

public enum Singleton {
    INSTANCE;

    public String nombre(){
           return this.getClass().getName();
    }
}

Este enfoque es funcionamente equivalente a los dos anteriores, excepto porque es más conciso y ofrece una rigurosa garantía contra múltiples instanciaciones, ya que con los métodos anteriores podíamos invocar al constructor privado mediante reflexión y obtener más de una instancia. En el siguiente ejemplo, creamos por reflexión dos enums:

public static void main(String[] args) {
  Constructor<Singleton> ctor = Singleton.class.getDeclaredConstructor(String.class, int.class);
  Method acqMethod = ctor.getClass().getDeclaredMethod("acquireConstructorAccessor");
  acqMethod.setAccessible(true);
  acqMethod.invoke(ctor);
  Field accessorField = ctor.getClass().getDeclaredField("constructorAccessor");
  accessorField.setAccessible(true);
  Object accessor = accessorField.get(ctor);
  Method newInstanceMethod = accessor.getClass().getMethod("newInstance", Object[].class);
  newInstanceMethod.setAccessible(true);
  Singleton singleton1 = (Singleton) newInstanceMethod.invoke(accessor, new Object[]{new Object[]{"hey", 1}});
  Singleton singleton2 = (Singleton) newInstanceMethod.invoke(accessor, new Object[]{new Object[]{"hey", 1}});
 
  if(singleton1 == singleton2){
  	System.out.println("son iguales los objetos");
  }else{
  	System.out.println("no son iguales los objetos");
  }
 
  if(singleton1.INSTANCE == singleton2.INSTANCE){
	System.out.println("son iguales las instancias");
  }else{
	System.out.println("no son iguales las instancias");
  }
}

Como resultado de ejecutar el código, obtenemos dos objetos distintos, pero las instancias de su interior son la misma y por tanto mediante reflexión no hemos roto la singleton.

Además dado que los enumerados implementan la interfaz serializable no necesitamos hacer nada adicional para serializar este objeto.

Según Joshua Bloch es la mejor manera de instanciar una singleton.