Los llamados patrones de diseño ayudan a los de­sa­rro­lla­do­res en la pro­gra­ma­ción orientada a objetos pro­po­r­cio­na­n­do pla­n­ti­llas probadas y co­m­pro­ba­das para resolver tareas de pro­gra­ma­ción. Una vez se­le­c­cio­na­da la plantilla más adecuada a partir de los apro­xi­ma­da­me­n­te setenta patrones de diseño, se refina haciendo ada­p­ta­cio­nes in­di­vi­dua­les. Sin embargo, el enfoque general para el patrón sigue siendo el mismo. El patrón singleton es muy poderoso, pero tiene la repu­tación de ser una reliquia en la pro­gra­ma­ción orientada a objetos. En este artículo, te pre­se­n­ta­mos sus fo­r­ta­le­zas y de­bi­li­da­des, y te mostramos cómo se utiliza en la pro­gra­ma­ción.

¿Qué es el patrón singleton?

El patrón singleton, o singleton pattern en inglés, pertenece a la categoría de patrones creativos dentro del grupo de los patrones de diseño. También se le conoce si­m­ple­me­n­te como “singleton”. El propósito de este patrón es evitar que sea creado más de un objeto por clase. Esto se logra creando el objeto deseado en una clase y re­cu­pe­rá­n­do­lo como una instancia estática. El singleton es uno de los patrones más simples, pero más poderosos en el de­sa­rro­llo de software.

Cita

La “Gang of Four” (GoF), banda de los cuatro en español, un equipo de pro­gra­ma­do­res de los EE. UU. dice esto sobre el patrón singleton: “Asegúrate de que una clase tenga exac­ta­me­n­te una copia y provea un punto de acceso global a ella.”

¿Cuáles son las ca­ra­c­te­rí­s­ti­cas pri­n­ci­pa­les del patrón singleton?

Si se utiliza el patrón singleton para crear una instancia de una clase, entonces el patrón se asegura de que realmente sólo pe­r­ma­ne­z­ca con esta instancia única. El singleton hace que esta clase de software sea accesible glo­ba­l­me­n­te. En los di­fe­re­n­tes lenguajes de pro­gra­ma­ción, hay di­fe­re­n­tes métodos para lograrlo. Para ase­gu­rar­se de que pe­r­ma­ne­z­ca con una sola instancia única, se debe impedir que los usuarios creen nuevas in­s­ta­n­cias. Esto se logra mediante el co­n­s­tru­c­tor, de­cla­ra­n­do el patrón como “privado”. Esto significa que sólo el código en el singleton puede in­s­ta­n­ciar el singleton en sí mismo. Por lo tanto, esto garantiza que sólo un mismo objeto puede llegar al usuario. Si esta instancia ya existe, no se crea ninguna nueva instancia. Un posible singleton pattern se ve así:

public class Singleton {
	private static Singleton instance; // protected from external access and static
	private Singleton() {} // private constructor with external access protection
	public static  getInstance() { // public method, call out by code
		if (instance == null) { // only if no instance exists, then create a new
			instance = new Singleton();
		}
		return instance;
	}
}

El patrón singleton: una re­pre­se­n­ta­ción en el diagrama UML

En el siguiente diagrama, diseñado uti­li­za­n­do UML, ob­se­r­va­mos todo el patrón de diseño de Singleton, que se compone de un solo objeto, ya que sólo es necesario crear una única instancia de una clase.

Desde el exterior, no es posible cambiar ninguna parte de la pieza única que se ha creado. Justo este es el objetivo cuando se utiliza el patrón singleton.

Ventajas y de­s­ve­n­ta­jas del singleton pattern

Una visión general de las ventajas

Al no estar poblado de in­nu­me­ra­bles variables (globales), un singleton puede es­cri­bi­r­se de forma rápida y sencilla. El patrón encapsula su creación, lo que significa que también puede ejercer un control preciso sobre cuándo y cómo se accede a él. Un patrón singleton existente puede derivarse mediante subclases para cumplir nuevas fu­n­cio­na­li­da­des. La fu­n­cio­na­li­dad que se utiliza se decide di­ná­mi­ca­me­n­te. Y, por último, pero no menos im­po­r­ta­n­te, un singleton se crea exac­ta­me­n­te cuándo se necesita, una ca­ra­c­te­rí­s­ti­ca que se denomina lazy loading. El proceso de in­s­ta­n­ciar un singleton an­te­rio­r­me­n­te, es decir antes de que se necesite, por otro lado, se llama carga ansiosa.

Una visión general de las de­s­ve­n­ta­jas

El uso des­inhi­bi­do de los si­n­gle­to­ns conduce a un estado similar al de la pro­gra­ma­ción pro­ce­di­me­n­tal (es decir, el no orientado a objetos), y puede llevar a ensuciar el código fuente. La di­s­po­ni­bi­li­dad global de patrones singleton plantea riesgos si se manejan datos sensibles. Esto porque si se hacen cambios en el singleton, no se podrá rastrear qué partes del programa están afectadas. Esto dificulta el ma­n­te­ni­mie­n­to de software, porque los fallos de fu­n­cio­na­mie­n­to son difíciles de rastrear. La di­s­po­ni­bi­li­dad global del patrón también dificulta la eli­mi­na­ción de los singleton, ya que los co­m­po­ne­n­tes del software siempre pueden referirse a este singleton. En apli­ca­cio­nes con muchos usuarios (apli­ca­cio­nes mu­l­tiu­sua­rio), un patrón singleton puede reducir el re­n­di­mie­n­to del programa, porque re­pre­se­n­ta un atasco de datos, al ser singular.

El patrón singleton “en la vida real”

El singleton se utiliza pri­n­ci­pa­l­me­n­te cuando hay que completar tareas re­cu­rre­n­tes en la rutina de un programa. Esto incluye los datos que tienen que ser escritos en un archivo, por ejemplo, durante el registro, o los trabajos de impresión que tienen que ser escritos en una sola memoria in­te­r­me­dia de la impresora una y otra vez. Dado que los co­n­tro­la­do­res y los me­ca­ni­s­mos de caché también tienen procesos re­cu­rre­n­tes, el patrón singleton se utiliza co­mú­n­me­n­te para éstos también.

Puesto que es muy difícil probar el patrón singleton, ilu­s­tra­re­mos su fu­n­cio­na­mie­n­to con el ejemplo de una pequeña empresa en la que varios empleados utilizan una sola impresora. Un ejemplo cercano a la práctica se presenta en la Serie de Tu­to­ria­les de Patrones de Diseño de Daniel H. Jacobsen. El patrón singleton que mostramos a co­n­ti­nua­ción se basa en esto.

Si un usuario envía una solicitud a la impresora, el patrón singleton hace la “pregunta”: “¿Ya hay un objeto de la impresora? Si no, entonces crea uno.” Esto se resuelve con un if/then-statement (devolver impresora == cero ?). Para evitar el acceso y los cambios, las variables in­di­vi­dua­les y la impresora se es­ta­ble­cen como “privadas” en lugar de “públicas”.

public class impresora {
	private static impresora;
	private int NúmeroPáginas;
	private impresora() {
	}
	public static impresora getInstance() {
		return impresora == Null ? 
				impresora = new impresora() : 
				impresora;
	}
	public void print(String text){
		System.out.println(text +
				"\n" + "número de páginas impresas hoy" + ++ NumeroPáginas +
				"\n" + "---------");
	}
}

El siguiente paso es “en­ca­p­su­lar” a los empleados de la sucursal. Las cadenas para los nombres, posición y función dentro de la compañía también se es­ta­ble­cen como “privados”.

public class Employee {
	private final String name;
	private final String position;
	private final String role;
	public empleado(String name, String position, String role) {
		this.nombre = nombre;
		this.posición = posición;
		this.función = función;
	}
	public void printCurrent función (){
		impresora = impresora.getInstance();
		impresora.print("empleado: " + nombre + "\n" +
			"Posición: " + posición + "\n" +
			" Función: " + función + "\n");
	}
}

Fi­na­l­me­n­te, los dos singleton se integran en una rutina de salida.

public class Main {
	public static void main(String[] args) {
		Empleado andreas = new empleado ("Andreas",
				"Jefe",
				"Gestiona la sucursal");
		Empleado julia = new empleado ("Julia",
				"Consultor",
				"Asesora a los clientes sobre las quejas");
		Empleado tom = new empleado ("Tom",
				"Venta",
				"Vende los productos");
		Empleado stefanie = new empleado ("Stefanie",
				"Desarrollador",
				" Mantenimiento informático en la sucursal.");
		Empleado matthias = new empleado ("Matthias",
				"Contable",
				"contabilidad financiera de la sucursal.");
		andreas.printCurrentFunción ();
		julia.printCurrentFunción ();
		tom.printCurrentFunción ();
		stefanie.printCurrentFunción ();
		matthias.printCurrentFunción ();
	}
}
Ir al menú principal