Você está na página 1de 8

Universidad Nacional Mayor de San Marcos Patrones de Construcción de Sistemas

Facultad de Ingeniería de Sistemas Guía de Laboratorio 101


EP Ingeniería de Sistemas Patrón Singleton

PATRÓN SINGLETON
Objetivo
El objetivo de la guía es el de afianzar el conocimiento acerca del patrón Singleton para
resolver problemas reales con el uso del lenguaje de programación Java. Además le
presentará las diferentes variantes de dicho patrón y los pros y contras de cada una.

Competencias
● Entender el concepto de patr\'on Singleton
● Utilizar el lenguaje Java para implementar el patr\'on
● Resolver problemas reales mediante el uso del patr\'on

Fundamentos

Usos del Patrón


El patrón Singleton restringe la instanciación de una clase y asegura que solo una instancia
de la clase exista en la JVM. La clase singleton debe proveer un mecanismo de acceso
global a dicha instancia. Este patrón es usado para el manejo de un Log, objetos de tipo
driver, “cache” y pool de hilos.
También es utilizado junto con otros patrones de diseño como Factoría Abstracta,
Constructor, Prototipo, Fachada, etc. Es utilizado dentro de algunas clases del núcleo de
Java como son: java.lang.runtime y java.awt.Desktop.

Conceptos clave
Para implementar el patrón Singleton, tenemos diferentes enfoques pero todos tienen en
común los siguientes conceptos:
● Un constructor privado para restringir la instanciación de la clase desde otras clases.
● Una variable estática privada de la misma clase la cual es la única instancia de la
clase.
● Un método estático público que retorna la instancia de la clase, este es el punto de
acceso global para que el resto del mundo pueda obtener la instancia de la clase
singleton.

1/4/2017 0 de 8
Universidad Nacional Mayor de San Marcos Patrones de Construcción de Sistemas
Facultad de Ingeniería de Sistemas Guía de Laboratorio 101
EP Ingeniería de Sistemas Patrón Singleton

Enfoques para implementarlo

Inicialización anticipada
La instancia de la clase es creada cuando se carga la clase, es el método más fácil para
crear un singleton pero el inconveniente es que la instancia es creada aún cuando la
aplicación cliente no la utiliza.
package com.journaldev.singleton;

public class EagerInitializedSingleton {

private static final EagerInitializedSingleton instance = new


EagerInitializedSingleton();

//private constructor to avoid client applications to use constructor


private EagerInitializedSingleton(){}

public static EagerInitializedSingleton getInstance(){


return instance;
}
}

Si la clase singleton no utiliza muchos recursos, este es el enfoque a utilizar. Pero muchos
escenarios las clases Singleton son creadas para recursos tales como Sistema de Archivos,
conexiones a Bases de Datos, etc. y nosotros debemos evitar su instanciación hasta que
por lo menos el cliente llame al método getInstance. Además este método no proporciona
manejo de excepciones.

Inicialización dentro de un bloque estático


Es similar al anterior excepto que la instancia de la clase es creada dentro de un bloque
estático que provee la opción de manejo de excepciones.

package com.journaldev.singleton;

public class StaticBlockSingleton {

private static StaticBlockSingleton instance;

private StaticBlockSingleton(){}

//static block initialization for exception handling


static{
try{
instance = new StaticBlockSingleton();
}catch(Exception e){
throw new RuntimeException("Exception occured in creating singleton instance");
}
}

public static StaticBlockSingleton getInstance(){


return instance;

1/4/2017 1 de 8
Universidad Nacional Mayor de San Marcos Patrones de Construcción de Sistemas
Facultad de Ingeniería de Sistemas Guía de Laboratorio 101
EP Ingeniería de Sistemas Patrón Singleton

}
}

Tanto este enfoque como el anterior crean la instancia aún antes de ser utilizada y no es la
mejor práctica a utilizar.

Inicialización relajada
Este método crea la instancia en el método de acceso global.

package com.journaldev.singleton;

public class LazyInitializedSingleton {

private static LazyInitializedSingleton instance;

private LazyInitializedSingleton(){}

public static LazyInitializedSingleton getInstance(){


if(instance == null){
instance = new LazyInitializedSingleton();
}
return instance;
}
}

Esta implementación trabaja bien en el caso de un entorno con un solo hilo de ejecución,
pero cuando se implementa dentro de sistema con múltiples hilos puede causar problemas
si varios hilos están dentro de if al mismo tiempo, ya que pueden destruir la instancia y
ambos hilos recibirán diferentes instancias de la clase.

Singleton seguro para los hilos


La manera más simple de crear un singleton seguro para hilos es crear un método de
acceso global de tipo sinchronized tal que sólo un hilo pueda ejecutar este método a la vez.

package com.journaldev.singleton;

public class ThreadSafeSingleton {

private static ThreadSafeSingleton instance;

private ThreadSafeSingleton(){}

public static synchronized ThreadSafeSingleton getInstance(){


if(instance == null){
instance = new ThreadSafeSingleton();
}
return instance;
}

1/4/2017 2 de 8
Universidad Nacional Mayor de San Marcos Patrones de Construcción de Sistemas
Facultad de Ingeniería de Sistemas Guía de Laboratorio 101
EP Ingeniería de Sistemas Patrón Singleton

Esto trabaja bien y provee seguridad en la ejecución con hilos pero reduce la performance
debido al costo asociado con el método sinchronized, el cual sólo necesitamos para los
pocos primeros hilos que puedan crear instancias separadas. Para evitar esta sobrecarga
extra utilizamos el principio de cerradura de doble chequeo (doble checking locking). En
este enfoque el bloque sinchronized es usado dentro de la condición if con un chequeo
adicional para asegurar que sólo una instancia de la clase singleton sea creada.

public static ThreadSafeSingleton getInstanceUsingDoubleLocking(){


if(instance == null){
synchronized (ThreadSafeSingleton.class) {
if(instance == null){
instance = new ThreadSafeSingleton();
}
}
}
return instance;
}

La implementación de Bill Pugh


Antes de Java 5 el modelo de memoria de java tenia una cantidad de problemas y los
enfoques anteriores fallaban en ciertos escenarios donde muchos hilos intentaban obtener
la instancia del singleton de manera simultánea. Hasta Bill Pugh vino con un enfoque
diferente para crear el Singleton utilizando una clase estática de ayuda interna. La
implementación de Bill es la siguiente:

package com.journaldev.singleton;

public class BillPughSingleton {

private BillPughSingleton(){}

private static class SingletonHelper{


private static final BillPughSingleton INSTANCE = new BillPughSingleton();
}

public static BillPughSingleton getInstance(){


return SingletonHelper.INSTANCE;
}
}

La clase interna contiene la instancia de la clase singleton. Cuando la clase es cargada


SingletonHelper no será cargada en memoria y solo lo hará cuando se llame al método
getInstance, esta clase es cargada y crea la instancia.
Este es el enfoque más usado para clases singleton que no requiere sincronización.

Usar Reflexión para destruir el patrón Singleton


La reflexión puede ser utilizada para destruir todas los enfoques anteriores de
implementación de un singleton.

1/4/2017 3 de 8
Universidad Nacional Mayor de San Marcos Patrones de Construcción de Sistemas
Facultad de Ingeniería de Sistemas Guía de Laboratorio 101
EP Ingeniería de Sistemas Patrón Singleton

package com.journaldev.singleton;

import java.lang.reflect.Constructor;

public class ReflectionSingletonTest {

public static void main(String[] args) {


EagerInitializedSingleton instanceOne = EagerInitializedSingleton.getInstance();
EagerInitializedSingleton instanceTwo = null;
try {
Constructor[] constructors =
EagerInitializedSingleton.class.getDeclaredConstructors();
for (Constructor constructor : constructors) {
//Below code will destroy the singleton pattern
constructor.setAccessible(true);
instanceTwo = (EagerInitializedSingleton) constructor.newInstance();
break;
}
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(instanceOne.hashCode());
System.out.println(instanceTwo.hashCode());
}

Cuando se ejecuta este ejemplo Usted notará que el hashCode para ambas instancias no
es el mismo, lo que destruye el patrón Singleton. La Reflexión es muy potente y es utilizada
en gran cantidad de frameworks tales como Spring e Hibernate. Revise
http://www.journaldev.com/1789/java-reflection-example-tutorial

Singleton con Enumeración (Enum Singleton)


Para solucionar esta situación con la reflexión, Joshua Bloch sugiere el uso de Enum para
implementar el patrón de diseño Singleton porque Java asegura que cualquier valor de tipo
enum es instanciado solamente una vez en un programa Java. Como los valores enum de
Java son globalmente accesibles, vienen a ser un Singleton. La deeventaja es que el tipo de
dato enum es algo inflexible: por ejemplo no permite la inicialización relajada.

package com.journaldev.singleton;

public enum EnumSingleton {

INSTANCE;

public static void doSomething(){


//do something
}
}

1/4/2017 4 de 8
Universidad Nacional Mayor de San Marcos Patrones de Construcción de Sistemas
Facultad de Ingeniería de Sistemas Guía de Laboratorio 101
EP Ingeniería de Sistemas Patrón Singleton

La Serialización y el Singleton
A veces en sistemas distribuidos nosotros necesitamos implementar la interfaz Serializable
dentro de la clase Singleton de manera que podamos almacenar su estado en en disco y
recuperarlo posteriormente en otro punto en el tiempo. Aqui hay un pequeño singleton que
implementa la interfaz Serializable.

package com.journaldev.singleton;

import java.io.Serializable;

public class SerializedSingleton implements Serializable{

private static final long serialVersionUID = -7604766932017737115L;

private SerializedSingleton(){}

private static class SingletonHelper{


private static final SerializedSingleton instance = new SerializedSingleton();
}

public static SerializedSingleton getInstance(){


return SingletonHelper.instance;
}

El problema con el singleton serializado es que nosotros lo recuperemos, obtendremos una


nueva instancia de la clase. Veamos esto con un simple programa de prueba:

package com.journaldev.singleton;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;

public class SingletonSerializedTest {

public static void main(String[] args) throws FileNotFoundException, IOException,


ClassNotFoundException {
SerializedSingleton instanceOne = SerializedSingleton.getInstance();
ObjectOutput out = new ObjectOutputStream(new FileOutputStream(
"filename.ser"));
out.writeObject(instanceOne);
out.close();

//deserailize from file to object


ObjectInput in = new ObjectInputStream(new FileInputStream(
"filename.ser"));
SerializedSingleton instanceTwo = (SerializedSingleton) in.readObject();

1/4/2017 5 de 8
Universidad Nacional Mayor de San Marcos Patrones de Construcción de Sistemas
Facultad de Ingeniería de Sistemas Guía de Laboratorio 101
EP Ingeniería de Sistemas Patrón Singleton

in.close();

System.out.println("instanceOne hashCode="+instanceOne.hashCode());
System.out.println("instanceTwo hashCode="+instanceTwo.hashCode());

Si ejecuta este programa podrá ver que el valor de hashCode es distinto para ambas
instancias del Singleton, lo cual destruye el patrón, para solucionar esto necesitamos
proveer la implementación del método readResolve()

protected Object readResolve() {


return getInstance();
}

Después de esto notara que el hashCode de ambos será el mismo dentro del programa de
prueba.

Lecturas recomendadas
● Java Sincronización ​http://www.journaldev.com/1061/thread-safety-in-java
● Java clases internas estáticas de ayuda
http://www.journaldev.com/996/java-inner-class
● Java clases anidadas ​http://www.journaldev.com/996/java-inner-class
● Java Reflexión ​http://www.journaldev.com/1789/java-reflection-example-tutorial
● Java enum ​http://www.journaldev.com/716/java-enum
● Java Serialización ​http://www.journaldev.com/927/how-to-write-object-to-file-in-java
● Java Deserialización
http://www.journaldev.com/933/how-to-read-object-from-file-in-java

Ejercicios

Requisitos:
Instale mysql server y mysql server workbench ​https://dev.mysql.com/downloads/mysql/
Instale la base de datos de prueba llamada sakila.
https://dev.mysql.com/doc/sakila/en/sakila-installation.html
Obtenga la consulta Find Overdue DVD de los ejemplos de Sakila
https://dev.mysql.com/doc/sakila/en/sakila-usage.html

1/4/2017 6 de 8
Universidad Nacional Mayor de San Marcos Patrones de Construcción de Sistemas
Facultad de Ingeniería de Sistemas Guía de Laboratorio 101
EP Ingeniería de Sistemas Patrón Singleton

Pasos a seguir
1. En netbeans cree un nuevo proyecto de tipo Java Web -> Web Application. El
nombre de la aplicación será Sakila1. Ubique el directorio donde colocará su
proyecto y no utilice ningún framework.
2. Una vez creado el proyecto proceda a crear una clase llamada “Conexion” la cual
debe instanciar sólo un objeto de tipo conexión el cual se utilizará a lo largo del
proyecto para realizar operaciones con la base de datos Sakila.
3. Luego cree una página denominada index.jsp donde obtenga la conexión y ejecute
la consulta “Find Overdue DVD” de los ejemplos de sakila para obtener los datos y
presentarlos en la pantalla.

Bibliografía
1. Java Singleton Design Pattern Best Practices with Examples
http://www.journaldev.com/1377/java-singleton-design-pattern-best-practices-exampl
es
2. Simply Singleton
http://www.javaworld.com/article/2073352/core-java/simply-singleton.html

1/4/2017 7 de 8

Você também pode gostar