Saltar al contenido principal

Singleton

Es un patrón de diseño que permite restringir la creación de objetos pertenecientes a una clase. Su intención consiste en garantizar que una clase solo tenga una instancia y proporcionar un punto de acceso global a ella.

Para realizar esto el patrón define que:

  • La propia clase es la responsable de crear la única instancia.
  • Permite el acceso global a dicha instancia mediante un método de clase.
  • Declara el constructor de la clase como privado para que no sea instanciable directamente.

Ejemplo

Veamos un ejemplo. Vamos a crear una clase que implemente este patrón para la gestión de personal de una empresa.

import java.util.ArrayList;

public class GestionPersonal {
// Instancia de la propia clase que será única
private static GestionPersonal INSTANCE;

// Atributos de la clase
private ArrayList<String> empleados;

// El constructor privado no permite que se genere un constructor por defecto
private GestionPersonal() {
// Inicializamos los atributos de la clase
this.empleados = new ArrayList<String>();
}

// Método público y estático para poder acceder a los métodos de la clase
public static GestionPersonal getInstance() {
//Si aún no se accedió, se inicializa
if(INSTANCE == null) {
INSTANCE = new GestionPersonal();
}
return INSTANCE;
}

// Esto permite que no se clone el objeto
public Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}

// Métodos de la clase
public void addEmpleado(String nombre) {
this.empleados.add(nombre);
}

public String listaEmpleados() {
String aux = new String();
for(String s:empleados) aux+= s + " - ";
return aux;
}
}

Uso de esta clase en el programa principal:

public static void main(String[] args) {
//Añadimos un nuevo empleado
GestionPersonal.getInstance().addEmpleado("Manuel");
GestionPersonal.getInstance().addEmpleado("Ramón");
System.out.println(GestionPersonal.getInstance().listaEmpleados());

}

Casos de uso reales

Algunos ejemplos de casos de uso reales del patrón Singleton son:

  • Gestión de conexión a bases de datos: para garantizar que solo exista una conexión compartida y evitar conflictos o consumo excesivo de recursos.
  • Gestor de configuración global: para centralizar los valores de configuración y asegurarse de que sean coherentes en toda la aplicación.
  • Controlador de impresoras o recursos físicos: para controlar el acceso a un único dispositivo y evitar operaciones simultáneas incompatibles.
  • Registro o Logger del sistema: para mantener un único punto de escritura en los archivos de log y un formato de registro uniforme.
  • Manejador de hilos o thread pool: para coordinar el uso de los hilos de forma controlada y evitar la creación innecesaria de nuevos hilos.
  • Caché o memoria compartida: para que todos los componentes accedan al mismo conjunto de datos temporales y se mantenga la coherencia.
  • Gestor de interfaz gráfica o ventana principal: para que solo exista una ventana principal que gestione la interacción y los eventos globales.
  • Sistemas de autenticación o sesión: para conservar de forma centralizada el estado del usuario autenticado durante la ejecución.
  • Controladores centrales en frameworks: para disponer de un núcleo único que coordine y gestione los diferentes componentes del sistema.