Deja de usar Clases para Singleton en Javascript

Cómo aplicar Singleton en Javascript

hace 10 meses   •   4 min de lectura

Por Andrés Tuñón
No somos de la misma clase 8)

Primero que todo, el título es un poco sarcasmo del contenido real del artículo.

Estuve investigando acerca de cómo aplicar Singleton en JS y me encontré muchos hipsters que aborrecen el uso de clases, a continuación plantearé mis hallazgos y perspectiva.

Problema que buscaba solucionar

Para utilizar correctamente el Firebase SDK en NodeJS, debes asegurarte que se inicialice 1 vez, de lo contrario te tirará un error.

Esto lo puedes solventar preguntando en una función por si la aplicación ya está iniciada (como puedes ver en la imagen); pero en js ¿soy capaz de asegurarme de que una línea de código sea ejecutada tan solo 1 vez? ¿qué tal si simplifico el código aplicando Singleton?

Singleton

Este título suena super pomposo; pero simplemente es una clase que aseguras que se instancie  1 sola vez; persistiendo el estado en toda la aplicación.

Singleton descriptive image
source: https://refactoring.guru/design-patterns/singleton

Un buen ejemplo es lo clásico de establecer la conexión a la base de datos, siempre buscas utilizar una y solo una clase con este propósito, no crear múltiples conexiones cada vez que llega una nueva solicitud.

De igual modo ocurre con Firebase, yo busco iniciar mi entorno de Firebase 1 sola vez e inclusive si no lo hago así; el mismo SDK me lanza un error:

Error - FirebaseAppError: The default Firebase app already exists. This means you called initializeApp() more than once without providing an app name as the second argument. In most cases you only need to call initializeApp() once. But if you do want to initialize multiple apps, pass a second argument to initializeApp() to give each app a unique name.

¿Cómo implementarlo en Javascript?

La forma más entendible, orientada a OOP que encontré fue esta:


export default class Singleton {
    private static instance: Singleton;


    private constructor() { }


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

        return Singleton.instance;
    }


    public someBusinessLogic() {
        // ...
    }
}

Como pueden apreciar, es cuestión de importar la clase y utilizar solo la función getInstance() siempre.

Aunque la verdad para mi queda un poco feo conociendo otras maneras; la siguiente para mí es más elegante y menos "verborreo":

class Singleton {


    constructor() { 
        // .. execute 1 time
    }


    public someBusinessLogic() {
        // ...
    }
}

export default new Singleton()

Aprovechando el hecho de que estoy en JS y utiliza módulos que se cachean al ser importados (solo se ejecutan 1 vez*), puedo exportar la instancia y asegurarme que siempre se utilizará ese instancia, ejecutando el constructor 1 sola vez.

Advertencia: Si importas esta clase con un nombre o incluso un case distinto, el sistema de JS lo comprenderá como distinto y se ejecutará otra vez. Me refiero a esto:

import Singleton 'from './Singleton';
import singleton from './Singleton';


Por esta razón, encontrarás en muchas fuentes que esta última versión es "no-production ready"; peeero yo no estoy creando una librería, asi que no me afecta.

Deja de usar Clases en Javascript

Esto de utilizar clases es nuevo en JS, es syntaxis sugar sobre el conocido uso de objetos antes de ES5 y tienen razón, es un hecho.

Por ejemplo el código de arriba podría ser reescrito así:

export default {
     // .. execute 1 time
     
     someBusinessLogic() {
        // ...
    }
}

Hay unos puristas del JS que no toleran que se utilice clases y la verdad me parece ridículo muchos de los artículos que he visto rondando; ya que para mí es simplemente otra forma de ordenar el código.

Ventajas de usar Clases

Si notas, no hay constructor y no es tan claro, incluso podrías colocar cualquier otro código regado por ahí y asegurarte que ejecutará 1 sola vez; pero podría ser un gran desorden.

Al tener que extender o heredar de una clase base es mucho mas claro y no olvidemos que al usar Typescript uno piensa más en OOP.

Desventajas de usar Clases

A veces es un overkill usar una clase si solo quieres agregar funciones o constantes; ya que, con una clase primero debes instanciarla y luego utilizarla.

En JS se suele trabajar con Functional Programming por su naturaleza y OOP suele romper eso y provocar efectos secundarios.

Mi opinión final

Usa las herramientas cuando se necesiten, dependiendo del contexto y evitando complicar el código sin necesidad.

Hay veces que es mejor usar clases; por ejemplo al trabajar con Typescript, inyección de dependencias en Angular y otros casos en el que el paradigma de OOP brilla; pero hay otros como exportar unas constantes o funciones en el que usando módulos termina siendo más conveniente por simplicidad.

Personalmente uso ambos y ni me di cuenta; pero para este caso con Firebase donde: veo prudente usar un constructor, mi entorno es Typescript, quiero aplicar Singleton y prefiero tener un mejor orden; me decantaré por usar una clase.

Referencias

Singleton
Singleton is a creational design pattern that lets you ensure that a class has only one instance, while providing a global access point to this instance.
El primer ejemplo y una de las imágenes provienen de acá
Singleton Pattern
Share a single global instance throughout our application
Donde vi múltiples opciones
Using classes - JavaScript | MDN
JavaScript is a prototype-based language — an object’s behaviors are specified by its own properties and its prototype’s properties. However, with the addition of classes, the creation of hierarchies of objects and the inheritance of properties and their values are much more in line with other objec…
Documentación oficial

Corre la voz

Sigue leyendo