Patrones de Diseño (VI): Patrones de Creación - Singleton

Hemos ido viendo a lo largo de los artículos cómo estructurar la creación de objetos en diferentes escenarios con distintas condiciones. Hoy toca definir el patrón Singleton, que trata de resolver la problemática que plantea el hecho de que un objeto sea único. Sin más preámbulo, vamos a abordar el patrón.

Singleton

Nombre: Singleton


Problema y Contexto:
Nuestro sistema requiere que una determinada clase sólo pueda ser instanciada una vez. Esto es útil cuando varios clientes desean utilizar una funcionalidad que es proporcionada por un único objeto. Nuestro objetivo será garantizar que la clase en cuestión no pueda generar más de una instancia y proporcionar un punto de acceso fijo a la misma.

Se aplica cuando:

  • Nuestro sistema tiene una clase que sólo deba ser instanciada una vez.
  • Dicha clase requiere de un punto de acceso global y conocido.


Solución y Estructura:

Para que éste escenario sea posible debemos asegurarnos que nuestra clase es instanciada si y sólo si no ha sido instanciada antes. Para controlar esta característica debemos hacer que el constructor sea privado, es decir, regulamos la instanciación de la clase dentro de la misma clase.

Para acceder a la clase debemos crear un atributo estático que represente una instancia de la misma, que debe ser de tipo privado. El acceso al atributo se hará desde un método público y estático que, en caso de no estar instanciado el atributo que representa la instancia, lo crea y en caso de que dicho atributo ya esté instanciado, lo devuelve.

La estructura es la siguiente:



Donde:

Singleton: Es la única clase que participa en este patrón (a parte de las clases cliente que la utilicen). Está compuesta de:

  • instance: atributo que representa la instancia única de la clase.
  • Singleton(): constructor privado de la clase.
  • getInstance(): método público que crea (en caso de no existir) o devuelve una instancia a la propia clase.



Consecuencias:

  • POSITIVAS:
    • Reduce el espacio de nombres, mejorando la utilidad proporcionada por las variables globales.
    • Control de acceso a la instancia única.
  • NEGATIVAS:
    • En sistemas distribuidos se debe establecer un control de acceso.


Patrones Relacionados: Abstract Factory, Monostate

Ejemplo:

Tenemos una empresa de fabricación de refrescos distribuida en varias sedes. Una sede central es la que se encarga de gestionar la distribución, por lo que debe llevar un control del stock de las demás sedes.

La sede central será representada mediante un Singleton al que podrán acceder las demás para actualizar su stock. La clase sería la siguiente:

public class SedePrincipal {
    private static SedePrincipal instance = new SedePrincipal();
    private int num_limonada = 0;
    private int num_cola = 0;

    // El constructor privado evita que se instancie esta clase fuera de la misma
    private Singleton() {}

    public static SedePrincipal getInstance() {
        return instance;
    }

    public int getNumLimonada()
    {
    	return this.num_limonada;
    }

    public int getNumCola()
    {
    	return this.num_limonada;
    }

    public void addLimonada(int num)
    {
    	this.num_limonada += num;
    }

    public void addCola(int num)
    {
    	this.num_cola += num;
    }
}


A continuación definiremos varios métodos main que simularán cómo distintas fabricas producen y actualizan su stock y luego consultan el stock total:

// Fabrica de limonada 1
public static void main(String[] args) {
	SedePrincipal sedeprinc = SedePrincipal.getInstance();
	
	int num_botellas_producidas = 55;
	sedeprinc.addLimonada(num_botellas_producidas);
	
	System.out.println(sedeprinc.getNumLimonada());
	// Imprimirá 55

	System.out.println(sedeprinc.getNumCola());
	// Imprimirá 0
	
}

// Fabrica de cola 1
public static void main(String[] args) {
	SedePrincipal sedeprinc = SedePrincipal.getInstance();
	
	int num_botellas_producidas = 208;
	sedeprinc.addCola(num_botellas_producidas);
	
	System.out.println(sedeprinc.getNumLimonada());
	// Imprimirá 55

	System.out.println(sedeprinc.getNumCola());
	// Imprimirá 208
	
}

// Fabrica de limonada 2
public static void main(String[] args) {
	SedePrincipal sedeprinc = SedePrincipal.getInstance();
	
	int num_botellas_producidas = 800;
	sedeprinc.addLimonada(num_botellas_producidas);
	
	System.out.println(sedeprinc.getNumLimonada());
	// Imprimirá 855

	System.out.println(sedeprinc.getNumCola());
	// Imprimirá 208
	
}

// Fabrica de cola 2
public static void main(String[] args) {
	SedePrincipal sedeprinc = SedePrincipal.getInstance();
	
	int num_botellas_producidas = 400;
	sedeprinc.addCola(num_botellas_producidas);
	
	System.out.println(sedeprinc.getNumLimonada());
	// Imprimirá 855

	System.out.println(sedeprinc.getNumCola());
	// Imprimirá 608
	
}

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP