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