Introduccion

1

Patrones de Diseño Creacionales en Software

2

Patrones Creacionales: Singleton, Factory, Abstract Factory, Builder, Prototype

Singleton

3

Patrón Singleton: Implementación y Uso en Programación Orientada a Objetos

4

Diagrama de Clases del Patrón Singleton en JavaScript

5

Diferencias entre JavaScript y TypeScript en patrones de diseño

6

Ventajas y desventajas del patrón Singleton en diseño de software

Factory

7

Patrones de Diseño: Introducción al Patrón Factory

8

Patrón Factory: Implementación y Detalles Esenciales

9

Implementación del Patrón Factory Method en JavaScript

10

Comparación del Patrón Factory en JavaScript y TypeScript

11

Patrón Factory: Ventajas y Desventajas en Desarrollo de Software

Abstract Factory

12

Patrón Abstract Factory: Estrategia para Múltiples Fábricas de Coches

13

Patrones de Diseño: Abstract Factory en Producción de Coches

14

Implementación del patrón Abstract Factory en JavaScript

15

Diferencias entre JavaScript y TypeScript en el patrón Abstract Factory

16

Patrón Abstract Factory: Ventajas y Desventajas

Builder

17

Patrón Builder: Diseño y Aplicación en Producción de Vehículos

18

Patrón Builder: Análisis de Diagrama y Clases Relacionadas

19

Implementación del Patrón Builder en Producción de Coches

20

Comparación del Patrón Builder en JavaScript vs TypeScript

21

Patrón Builder: Ventajas, Desventajas y Aplicaciones Prácticas

Prototype

22

Patrón Prototype: Clonación de Objetos en Diseño de Software

23

Patrón Prototype en JavaScript y TypeScript

24

Implementación del Patrón Prototype en JavaScript

25

Comparación de Prototype en JavaScript y TypeScript

26

Patrón Prototype: Ventajas y Desafíos en Diseño de Software

Conclusiones

27

Patrones Creacionales en Diseño de Software

No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Diagrama de Clases del Patrón Singleton en JavaScript

4/27
Recursos

¿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:

  1. 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.

  2. 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:

class Singleton {
    constructor(version) {
        this.version = version;
    }
    
    static getInstance(version) {
        if (!Singleton.instance) {
            Singleton.instance = new Singleton(version);
        }
        return Singleton.instance;
    }
}

// Prueba del patrón Singleton
const singleton1 = Singleton.getInstance("versión1");
const singleton2 = Singleton.getInstance("versión2");

console.log(singleton1 === singleton2); // Output: true

¿Cómo se realiza la verificación de instancia?

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:

  1. Verificación: Se comprueba si Singleton.instance está definida. Si no lo está, se crea una nueva instancia.
  2. Asignación: La nueva instancia se asigna a Singleton.instance.
  3. 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:

// Verificación adicional
const singleton3 = Singleton.getInstance("versión3");

console.log(singleton1 === singleton3); // Output: true

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.

Aportes 22

Preguntas 2

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

 class Singleton {
    static instance = undefined; //Atributo estático para almacenar el valor, llamado para la validación de getInstance() 

    constructor(version) {
        this.version = version;
    }

    static getInstance(version) {
        //Si no existe el atributo instance...
        if (!Singleton.instance) {
            Singleton.instance = new Singleton(version); //...lo crea. 
        }
        return Singleton.instance;
    }

}

function appSingleton() {
    //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); //true
    console.log(singleton1 === singleton3); //true
}
appSingleton();

Técnica para evitar el uso de new Singleton:

class Singleton {
	constructor() {
		throw new Error('No constructor in this house!')
	}
}

Les dejo el Singleton en Java

Pseudocódigo:


.

Diagrama:

Para los que les gusta TS como a mi, aqui el code adaptado:

class Singleton {
    static instance : Singleton | undefined;
    version:string

    constructor(version: string){
        this.version = version;
    }

    static getInstance(version:string){
        if(!Singleton.instance){
            Singleton.instance = new Singleton(version);
        }

        return Singleton.instance;
    }
}

function appSingleton() {
    //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); //true
    console.log(singleton1 === singleton3); //true
}
appSingleton();
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.

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”.

function appSingleton() {
  const singleton1 = Singleton.getInstance("version-1");
  const singleton2 = Singleton.getInstance("version-2");
  const singleton3 = Singleton.getInstance("version-3");

  console.log(singleton1 === singleton2);
  console.log(singleton1 === singleton3);
  console.log(singleton3.version); // version-1
}
Así fue como lo había aprendido a hacerlo ```js class Singleton{ static instance = undefined constructor(version){ if(Singleton.instance){ return Singleton.instance } this.version = version Singleton.instance = this } } ```class Singleton{    static instance = undefined    constructor(*version*){        if(Singleton.instance){           return Singleton.instance         }        this.version = *version*        Singleton.instance = this    }}con
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.

Diagrama del patrón Singleton.

  1. Hacer que el constructor sea privado.
  2. Crear un método estático que llame al constructor privado, internamente, y que guarde la instancia en una variable estática.
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: ```javascript const Singleton = (function() { let instance; function createInstance() { return { /* Propiedades del singleton */ }; } return { getInstance: function() { if (!instance) { instance = createInstance(); } return instance; } }; })(); // Uso const instance1 = Singleton.getInstance(); const instance2 = Singleton.getInstance(); console.log(instance1 === instance2); // true ``` 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 `constructor` **devuelve 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.
```js class ConexionDataBase { static instance = undefined; constructor(conexionString) { this.conexionString = this.conexionString; } static getInstance(conexionString) { if(!ConexionDataBase.instance){ ConexionDataBase.instance = new ConexionDataBase(conexionString); } else { console.log("Usando la conexión existente."); } return ConexionDataBase.instance; } } function appSingleton() { const conexionDataBase1 = ConexionDataBase.getInstance("cadena-de-conexion-1"); console.log(conexionDataBase1); const conexionDataBase2 = ConexionDataBase.getInstance("cadena-de-conexion-2"); console.log(conexionDataBase2); console.log(conexionDataBase1 === conexionDataBase2) } appSingleton(); ```class ConexionDataBase {    static instance = undefined;     constructor(conexionString) {        this.conexionString = this.conexionString;    }     static getInstance(conexionString) {        if(!ConexionDataBase.instance){            ConexionDataBase.instance = new ConexionDataBase(conexionString);        } else {            console.log("Usando la conexión existente.");        }        return ConexionDataBase.instance;    }} function appSingleton() {    const conexionDataBase1 = ConexionDataBase.getInstance("cadena-de-conexion-1");    console.log(conexionDataBase1);     const conexionDataBase2 = ConexionDataBase.getInstance("cadena-de-conexion-2");    console.log(conexionDataBase2);     console.log(conexionDataBase1 === conexionDataBase2)} appSingleton();
class ConexionDataBase {    static instance = undefined;     constructor(conexionString) {        this.conexionString = this.conexionString;    }     static getInstance(conexionString) {        if(!ConexionDataBase.instance){            ConexionDataBase.instance = new ConexionDataBase(conexionString);        } else {            console.log("Usando la conexión existente.");        }        return ConexionDataBase.instance;    }} function appSingleton() {    const conexionDataBase1 = ConexionDataBase.getInstance("cadena-de-conexion-1");    console.log(conexionDataBase1);     const conexionDataBase2 = ConexionDataBase.getInstance("cadena-de-conexion-2");    console.log(conexionDataBase2);     console.log(conexionDataBase1 === conexionDataBase2)} appSingleton();class ConexionDataBase {    static instance = undefined;     constructor(conexionString) {        this.conexionString = this.conexionString;    }     static getInstance(conexionString) {        if(!ConexionDataBase.instance){            ConexionDataBase.instance = new ConexionDataBase(conexionString);        } else {            console.log("Usando la conexión existente.");        }        return ConexionDataBase.instance;    }} function appSingleton() {    const conexionDataBase1 = ConexionDataBase.getInstance("cadena-de-conexion-1");    console.log(conexionDataBase1);     const conexionDataBase2 = ConexionDataBase.getInstance("cadena-de-conexion-2");    console.log(conexionDataBase2);     console.log(conexionDataBase1 === conexionDataBase2)} appSingleton();
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 ✨. <https://www.youtube.com/watch?v=rrWRhrdwuLg> 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!
Desde ES2022 Javascript soporta de manera nativa el uso de propiedades y métodos privados ✨. <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Private_properties> Se definen con el # delante del nombre de la propiedad o método que se quiere que sea privada.
Aqui os dejo otro ejemplo para entender mejor el patron de diseño Singleton: ```js class Configuracion { // Variable estática que contendrá la única instancia static datos = undefined; // Constructor privado para inicializar la configuración (tema, idioma) constructor() { this.tema = "light"; // Tema predeterminado this.idioma = "es"; // Idioma predeterminado } // Método estático para obtener la instancia única de Configuracion static obtenerInstancia() { if (!Configuracion.datos) { Configuracion.datos = new Configuracion(); } return Configuracion.datos; } // Método para cambiar el tema cambiarTema(nuevoTema) { this.tema = nuevoTema; console.log(`Tema cambiado a: ${this.tema}`); } // Método para cambiar el idioma cambiarIdioma(nuevoIdioma) { this.idioma = nuevoIdioma; console.log(`Idioma cambiado a: ${this.idioma}`); } } function appConfiguracion() { console.log("--- Inicializando Configuración Global ---"); // Obtenemos la instancia única de Configuración const configuracion1 = Configuracion.obtenerInstancia(); configuracion1.cambiarTema("Oscuro"); configuracion1.cambiarIdioma("Esp"); // Obtenemos otra vez la instancia para poder ver que los valores de config no han cambiado y se mantienen iguales const configuracion2 = Configuracion.obtenerInstancia(); // Comprobamos si ambas instancias son iguales console.log(`¿Configuración1 y Configuración2 son la misma instancia? ${configuracion1 === configuracion2 ? "Sí" : "No"}`); // Imprimimos los valores para comprobar que ambos acceden a la misma configuración console.log(`Configuración1 tema: ${configuracion1.tema}`); console.log(`Configuración2 tema: ${configuracion2.tema}`); console.log(`Configuración1 idioma: ${configuracion1.idioma}`); console.log(`Configuración2 idioma: ${configuracion2.idioma}`); } appConfiguracion(); ```
### Razones para Evitar la Lógica Singleton en el Constructor He visto en algunos ejemplos por internet, donde evitan usar el método getInstance() e implementan la logical del patron en el constructor de la clase, pero dejo algunas razones para no hacerlo. 1. Viola el Propósito del Constructor: * Los constructores están diseñados para inicializar nuevas instancias de una clase, no para controlar la cantidad de instancias o administrar la lógica de acceso a ellas. * Si incluyes la lógica del Singleton en el constructor, estás sobrecargando su propósito, lo que puede llevar a confusión y malas prácticas de diseño. 2. Dificulta el Control de Acceso: * El patrón Singleton funciona porque controla la creación de la instancia desde un método estático (`getInstance()`), manteniendo el constructor privado para que no se pueda acceder directamente desde fuera de la clase. * Si la lógica de creación se encuentra en el constructor, no se puede evitar que alguien intente crear una instancia usando `new`, lo que rompe el patrón. 3. Problemas de Mantenimiento y Claridad: * Mantener la lógica en el método `getInstance()` hace que sea más claro para cualquier desarrollador que lea tu código que esa es la única forma permitida de crear una instancia de la clase. * Dejar esta lógica en el constructor puede confundir, especialmente si el constructor sigue siendo accesible o no se maneja correctamente su visibilidad.
Implementación de Singleton en TS ```js class Singleton { private static instance: Singleton; // Hacer el constructor privado para prevenir la instanciación directa. private constructor() { console.log('Instancia creada'); } // Método estático para obtener la instancia única. public static getInstance(): Singleton { // Si la instancia no existe, la creamos. if (!Singleton.instance) { Singleton.instance = new Singleton(); } // Retornamos la instancia única. return Singleton.instance; } // Un método de ejemplo para la instancia. public someMethod(): void { console.log('Método de la instancia Singleton'); } } // Uso del Singleton const singleton1 = Singleton.getInstance(); const singleton2 = Singleton.getInstance(); singleton1.someMethod(); // Imprime: "Método de la instancia Singleton" // Comprobación de que ambas referencias apuntan a la misma instancia. console.log(singleton1 === singleton2); // Imprime: true ```class Singleton { ==================== # private static instance: Singleton; // Hacer el constructor privado para prevenir la instanciación directa. private constructor() { console.log('Instancia creada'); } // Método estático para obtener la instancia única. public static getInstance(): Singleton { // Si la instancia no existe, la creamos. if (!Singleton.instance) { Singleton.instance = new Singleton(); } // Retornamos la instancia única. return Singleton.instance; } // Un método de ejemplo para la instancia. public someMethod(): void { console.log('Método de la instancia Singleton'); } } // Uso del Singleton const singleton1 = Singleton.getInstance(); const singleton2 = Singleton.getInstance(); singleton1.someMethod(); // Imprime: "Método de la instancia Singleton" // Comprobación de que ambas referencias apuntan a la misma instancia. console.log(singleton1 === singleton2); // Imprime: true
no entendi nada
🟡 Se podría usar además la variable instance como privada, para evitar su modificación? ```js class Singleton { static #instance = undefined constructor(version){ this.version = version } static getInstance(version) { if (!Singleton.#instance) { Singleton.#instance = new Singleton(version) } return Singleton.#instance } } function appSingleton() { const singleton1 = Singleton.getInstance('version1') const singleton2 = Singleton.getInstance('version2') console.log(singleton1 === singleton2) // true } appSingleton() ```*class* Singleton {    *static* #instance = *undefined*     constructor(*version*){        this.version = *version*    }     *static* getInstance(*version*) {        *if* (!Singleton.#instance) {            Singleton.#instance = new Singleton(*version*)        }         *return* Singleton.#instance    }} function appSingleton() {    const singleton1 = Singleton.getInstance('version1')    const singleton2 = Singleton.getInstance('version2')        console.log(singleton1 === singleton2) *// true*} appSingleton()