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.