¿Qué es el diagrama de clases del patrón Singleton?
El patrón Singleton es un diseño en programación orientada a objetos que asegura que una clase solo tenga una instancia y proporciona un punto de acceso global. El diagrama de clases de Singleton nos presenta una representación gráfica que resume la estructura y comportamiento de este patrón. Dos aspectos destacados del diagrama son cruciales para su comprensión:
Constructor privado: El constructor de la clase se establece como privado. Esto se representa con un símbolo de menos (-), indicando que no puede ser accesible fuera de la propia clase. Este paso garantiza que no se pueden crear nuevas instancias de la clase utilizando el operador new.
Método estático getInstance(): Se crea un método estático que internamente llama al constructor privado. Este método es el que guarda la instancia en una variable estática y devuelve la misma instancia cada vez que se solicita.
¿Cómo se implementa el patrón Singleton en código?
La implementación en código del patrón Singleton suele seguir un proceso conocido y eficiente, que permite ahorrar recursos y asegurar la invariabilidad de la instancia. La traducción del diagrama a JavaScript ofrece una visión clara de cómo establecer el patrón, incluso cuando este lenguaje no soporta modificadores de acceso como private. El código es el siguiente:
El método estático getInstance() verifica si ya existe una instancia de la clase Singleton. Si no existe, crea una nueva instancia, la almacena, y la devuelve. Si ya existe, simplemente retorna la instancia existente:
Verificación: Se comprueba si Singleton.instance está definida. Si no lo está, se crea una nueva instancia.
Asignación: La nueva instancia se asigna a Singleton.instance.
Retorno: Se devuelve siempre Singleton.instance.
Este proceso garantiza que solo haya una única instancia activa a lo largo de toda la aplicación.
¿Cuál es el resultado al probar el patrón Singleton?
Al correr una prueba con varias llamadas a getInstance() y diferentes versiones, como se hace en el ejemplo anterior con singleton1 y singleton2, el resultado es crucial para validar el patrón. El código devuelve true, lo que significa que ambas variables apuntan a la misma instancia, cumpliendo así con el objetivo del patrón Singleton. A continuación se muestra un fragmento que refleja el testeo y su interpretación:
La prueba establece que no importa con qué versión se invoca el método getInstance(), siempre se retorna la misma instancia única. Esto demuestra la efectividad y fiabilidad del patrón Singleton. Como desarrollador, ahora tienes una herramienta poderosa para controlar y gestionar los estados globales en tus aplicaciones.
classSingleton{static instance =undefined;//Atributo estático para almacenar el valor, llamado para la validación de getInstance() constructor(version){this.version= version;}staticgetInstance(version){//Si no existe el atributo instance...if(!Singleton.instance){Singleton.instance=newSingleton(version);//...lo crea. }returnSingleton.instance;}}functionappSingleton(){//Todas las variables tienen la misma referencia al mismo objeto. 1 sola instancia a lo largo de la aplicación:const singleton1 =Singleton.getInstance('version-1')const singleton2 =Singleton.getInstance('version-2')const singleton3 =Singleton.getInstance('version-3')console.log(singleton1 === singleton2);//trueconsole.log(singleton1 === singleton3);//true}appSingleton();
Bien Carina, quedaron claros los conceptos de singleton y elementos estaticos, continua así!
Técnica para evitar el uso de new Singleton:
classSingleton{constructor(){thrownewError('No constructor in this house!')}}
!elmo
Afirmativo Diego, cuando sea un lenguaje tipado, arrojar errores es la clave
También se podría usar el propio constructor para retornar siempre la misma instancia, y no haría falta tener que armar otro método que haga de constructor
classSingleton{static instance =undefined;constructor(version){if(Singleton.instance)returnSingleton.instancethis.version= version
Singleton.instance=this}}
Les dejo el Singleton en Java
Nada como regresar a los inicios con Java
Pseudocódigo:
.
Diagrama:
Creo que por cuestiones didacticas seria mas conveniente empezar con los ejemplos y a partir de ahi ir identificando cada concepto, debido a que es mas dificil comprender los conceptos desde la abstraccion.
En el mundo real, para llegar a los patrones de diseno primero se dan los casos de uso y luego se identifican los patrones y crean los conceptos a partir de las ocurrencias repetidas.
Creo que seria valiosa esta metodologia para los cursos de programacion en general.
Gracias por el valiosisimo feedback Diego! Tomando nota!
Para los que les gusta TS como a mi, aqui el code adaptado:
classSingleton{staticinstance:Singleton|undefined;version:string
constructor(version: string){this.version= version;}staticgetInstance(version:string){if(!Singleton.instance){Singleton.instance=newSingleton(version);}returnSingleton.instance;}}functionappSingleton(){//Todas las variables tienen la misma referencia al mismo objeto. 1 sola instancia a lo largo de la aplicación:const singleton1 =Singleton.getInstance('version-1')const singleton2 =Singleton.getInstance('version-2')const singleton3 =Singleton.getInstance('version-3')console.log(singleton1)console.log(singleton2)console.log(singleton3)console.log(singleton1 === singleton2);//trueconsole.log(singleton1 === singleton3);//true}appSingleton();
Interesante Cris, solo una anotación, en este código el constructor continua siendo publico, es decir podria ser llamado en cualquier momento y crear una instancia nueva.
Para mitigar esto se hace el constructor privado y ahora si, se depende unicamente del metodo getInstance para el manejo de la instancia unica, recuerda, en singleton, solo hay un punto de acceso a la instancia.
Algo interesante es que al leer el valor de version de las todas las instancias, el valor siempre será el de la primera instancia que se ha creado. En este caso "version-1".
classSingleton{static instance =undefinedconstructor(version){if(Singleton.instance){returnSingleton.instance}this.version= version
Singleton.instance=this}}```classSingleton{static instance =undefinedconstructor(*version*){if(Singleton.instance){returnSingleton.instance}this.version=*version*Singleton.instance=this}}con
Bien es posible que la creacion de instancias se maneje dentro del constructor sin embargo, no es recomendable dado que se busca que el constructor sea completamente ajeno a cualquier logica que no este relacionada a la configuracion de los elementos internos de la instancia.
Tambien hay que considerar que en tu implementacion hay que regresar tambien en el caso en el que la instancia no esta inicializada.
Este patrón de diseño lo estuve utilizando sin saber que así se llamaba, creé una instancia para usar remote-config en un proyecto y funciona muy bien, no he visto a ninguno de mis colaboradores si quiera intentar usar el new() Me alegra poder conocer la teoría detrás de ello y poder argumentar su uso.
Excelente Edwin!
Diagrama del patrón Singleton.
Hacer que el constructor sea privado.
Crear un método estático que llame al constructor privado, internamente, y que guarde la instancia en una variable estática.
al dejarlo como en el minuto 13.07, no se está asignando la versión que se recibe, por lo tanto, al correr appSingleton() dará TRUE ya que undefined === undefined
class Logger {
constructor() {
if (Logger.instance) {
return Logger.instance;
}
this.logs = [];
Logger.instance = this;
}
addLog(message) {
this.logs.push(message);
}
countLogs() {
return this.logs.length;
}
}
Al no poder restringir el constructor, de esta forma te aseguras de no crear más instancias.
Según entiendo es como un closure pero en objetos...
Se almacena los requerimientos, pero solo se usa los métodos. Sí?
Consulta, en una instancia creada de una clase, que utiliza como una de sus propiedades en el constructor una propiedad estática de la misma clase, si esa propiedad estática cambia de valor a lo largo del código, en la instancia también cambiará?
Cambiaria para todas las instancias, asi es.
Como observacion, la idea de los elementos es justamente la de no cambiar, por eso su uso debe ser debidamente planificado.
En JavaScript, no hay un modificador de acceso "private" como en otros lenguajes. Sin embargo, puedes simular un constructor privado utilizando funciones y closures. Aquí te muestro un ejemplo con el patrón Singleton:
En este código, createInstance es una función que actúa como constructor privado, accesible solo a través de getInstance.
Pequeño glosario:
Singleton → Es un patrón de diseño que garantiza que solo haya una única instancia de una clase en toda la aplicación.
Instancia → Es un objeto creado a partir de una clase. En el patrón Singleton, siempre apunta al mismo objeto, evitando la creación de múltiples instancias.
Constructor → Es un método especial que se ejecuta automáticamente cuando se crea un nuevo objeto. En Singleton, se usa para controlar que solo haya una instancia.
Instancia única → Se refiere a que solo existe una copia del objeto en toda la aplicación, sin importar cuántas veces se intente crear una nueva.
Retorno de instancia → En Singleton, el constructordevuelve la misma instancia existente en lugar de crear una nueva.
Objeto inmutable → Es un objeto que no puede ser modificado después de su creación. En Singleton, a veces se usa Object.freeze() para evitar cambios en la instancia.
Patrón de diseño → Es una solución reutilizable para problemas comunes en el desarrollo de software. Singleton es uno de estos patrones y se usa para controlar el acceso a una única instancia de un objeto.
Object.freeze() → Es un método en JavaScript que hace que un objeto no pueda ser modificado después de su creación. Se usa a veces en Singleton para asegurar que la instancia no cambie.
Comparación (===) → Se usa para verificar si dos variables apuntan a la misma instancia del Singleton.
Una conexión a una base de datos es un buen ejemplo para este patrón
Además de la forma explicada por el Profesor Daniel en la clase acerca de cómo crear un Singleton en Javascript y siempre retornar la instancia "guardada en caché" mediante el método getInstance... Encontré este videíto super interesante en donde se implementa otra forma de hacerlo directamente con el mismo constructor ✨.
En el video, en vez de crear un método "getInstance" que siempre nos retorne la instancia de la clase, directamente se modifica el constructor de la clase para que cada vez que se utilice el operador new para crear una nueva instancia; en vez de que se cree una nueva, retorne en cambio la única instancia que existe "guardada en caché" (y si no existe ninguna guardad previamente, entonces sí la crea).
Es muy interesante ✨.
Si bien este método se "salta las reglas" ya que no hace que el constructor sea privado ni tampoco crea un método estático getInstance... Logra el mismo comportamiento al modificar la forma en la que se usa el constructor. ¡Y eso me parece super genial!