Design Patterns - Singleton

Propósito

El patrón Singleton garantiza que una clase sólo tenga una instancia, y proporciona un punto de acceso global a la misma.

Problema

La necesidad de controlar el acceso y la creación de una instancia de una clase. Es útil cuando una sola instancia de una clase debe ser compartida y coordinada a través de todo el sistema.

Solución

La solución que propone el Singleton es limitar la creación de una clase a un solo objeto y brindar un punto de acceso global a este objeto:

  • Instancia única: Garantiza que una clase tenga solo una instancia.
  • Acceso global: Brinda un acceso fácil y global a esa única instancia.

Estructura

singleton

Participantes

  • Singleton: Una clase que tiene un método para crear o acceder a su única instancia.

Los detalles a notar son:

  • Constructor privado: Esto hace que no se puedan generar instancias, ya que no se puede invocar por fuera de la propia clase.
  • Atributo estático de la propia clase: que el método estático getInstance() va a retornar al ser invocado.
  • Atributos y operaciones propias de la clase: lo que le da sentido a la instancia, los datos y operaciones que maneja.

Aplicando constructor privado y atributo estático de la propia clase con método estático que la retorne, podemos convertir una clase en Singleton.

Cuándo Usarlo

Este patrón es recomendable cuando:

  • Debe haber una sola instancia de una clase y debe ser accesible a los clientes desde cualquier punto.

Ventajas

Control de Instancias: Asegura que solo exista una instancia de una clase.

Reducción de Uso de Recursos: Evita la creación innecesaria de objetos, lo que puede ahorrar recursos.

Acceso Global: Ofrece un punto de acceso a la instancia desde cualquier parte del código.

Desventajas

Incumplimiento del principio de Responsabilidad Única: Puede llevar a una clase a tener múltiples responsabilidades, ya que agrega la responsabilidad de garantizar su única instancia.

Problemas en entornos multihilos: Se debe sincronizar el acceso a la única instancia en entornos concurrentes.

Ejemplo: Parámetros de configuración

Debemos desarrollar una clase para mantener los parámetros de configuración durante la ejecución del sistema, que deben ser accesibles globalmente y consistentes.

Problema

Los clientes deben poder acceder a los parámetros de configuración en todo momento, por lo que podríamos mantenerlos en un objeto, cargados al iniciar (para no tener que cargarlos más de una vez). Tener una sola instancia de este objeto podría ser una buena solución. 

Solución planteada

Implementamos un Singleton, porque nos va a garantizar una sola instancia del objeto que mantiene los parámetros de configuración, y nos brinda el acceso a la misma.

Creamos la clase Configuration que contiene los parámetros de configuración, y la convertimos en singleton, mediante un constructor privado, un atributo estático instance de la misma clase Configuration y un método estático de acceso a esta instancia getInstance().

Application es una de las clases que necesita acceder a los valores de los parámetros de configuración.

singleton

Código Java

Codificamos en Java lo que preparamos en el diagrama.

Definimos la clase singleton de la forma:


    public class Configuration {
      //Atributo estático del tipo de la clase
      private static Configuration instance; 
      //Atributos propios de la clase
      private String configValue;
  
      private Configuration() {
          // Constructor privado
          configValue = “Initial Config”;
      }
      //Método estático de acceso a la única instancia
      public static Configuration getInstance() {
          if (instance == null) {
              instance = new Configuration();
          }
          return instance;
      }
      //Métodos propios de la clase
      public String getConfigValue() {
          return configValue;
      }
  
      public void setConfigValue(String configValue) {
          this.configValue = configValue;
      }
    }
                

El código cliente, usa los prototipos para crear formas:


    public class Application {
      public static void main(String[] args) {
          Configuration config = Configuration.getInstance();
          System.out.println(config.getConfigValue());
          
          config.setConfigValue(“Updated Config”);
          Configuration anotherConfig = Configuration.getInstance();
          System.out.println(anotherConfig.getConfigValue()); // Mostrará “Updated Config”
      }
    }                  
                

Mapeo (del ejemplo a Participantes)

Los participantes que vimos antes son: Singleton, Application:

  • Configuration (Singleton): Mantiene los valores de los parámetros de configuración y garantiza su única instancia
  • Application (Client): Es la clase que usa los parámetros de configuración, accede al singleton mediante el método estático getInstance().

Conclusiones

Necesitábamos almacenar parámetros, vimos cómo hacerlo con un singleton, que nos garantiza un único objeto que los mantiene durante toda la ejecución y otorga el acceso al mismo.

Patrones relacionados

  • Factory Method, Abstract Factory, Builder

A veces se implementan como Singleton.

  • Facade

Puede implementarse como Singleton.