Moduli ES6 – Il singleton pattern
Già in passato ci è capitato di trattare il pattern Singleton, in Javascript (ES5+NodeJS). Vediamo oggi come implementarlo con il nuovo Javascript EcmaScript 6 (ES6).
I singleton per definizione sono quei moduli che non han bisogno di scrivere sul global space e che non han senso di esistere in più istanze.
Con ES6 si introducono nuovi costrutti e sintassi per esportare e importare nativamente moduli.
Il pattern
Creiamo una classe ed esportiamone un’istanza:
// File: yolo.js class Yolo {} export let yolo = new Yolo(); |
Per importarla, lo facciamo da un altro modulo. Sarà ES6 ad assicurarsi che l’istanza importata sia la stessa istanza importata anche altrove.
// File: laser.js import { yolo } from "./yolo.js"; // yolo is a single instance of Yolo class |
// File: another-file.js import { yolo } from "./yolo.js"; // same yolo as in laster.js |
Esempio
In questo esempio mostriamo:
1) Un modulo principale che contiene un altro modulo figlio.
2) Entrambi i moduli condividono la stessa istanza di un broker di notifica.
Per eseguire questo specifico esempio si richiedono le seguenti dipendenze:
$ npm install webpack babel-loader babel-preset-es2015 |
Classe di notifica
Creiamo una classe basilare in un file chiamato notifications.js. Il costruttore crea un array che conterrà i messaggi, mostrati successivamente.
Il secondo metodo chiamato add(message) inserisce un dato messaggio all’interno del nostro array.
// notifications.js export class Notifications { constructor() { this.messages = []; } add(message) { this.messages.push(message); } } |
Per motivi di debug aggiungiamo anche un document.write
ed un console.log
al metodo add:
add(message) { this.messages.push(message); // debug document.write(`<p>${this.messages.length} - ${message}</p>`); console.log('messages', this.messages); } |
Ogni volte che qualcuno chiama il metodo add otteniamo il numero di messaggi presenti nell’istanza della nostra classe.
E ci basta importare così la classe:
import { Notifications } from "./notifications.js“; |
Oltre ad esportare la classe, stiamo esportando una nuova istanza della classe. Usando “let” si esporterà sempre una e una sola istanza della classe.
export let notifications = new Notifications(); |
Questa è tutta la magia che ci sta dietro. Let usato nel global scope assicura che notifications non venga riusato. Così che ogni volta che facciamo import { notifications } from "./notifications.js";
otteniamo la stessa istanza della classe Notifications
.
Child-module
Il primo utilizzo che andiamo a fare della nostra classe di notifica è quello di importarla in un modulo-figlio. Quando questo modulo figlio viene istanziato aggiunge in automatico un messaggio nella classe di notifica:
// child.js import { notifications } from "./notifications.js"; export class Child { constructor(name) { this.name = name; notifications.add('yolo from ' + this.name) } } |
Main-Module
Mettiamo adesso tutto insieme creando un file chiamato main.js
che rappresenta il flusso d’esecuzione principale della nostra applicazione:
// main.js import { notifications } from "./notifications.js"; import { Child } from "./child.js"; export class Main { constructor() { notifications.add('yolo 1 from main'); // create new children // (they call notifications.add in constructor) let child1 = new Child('le child 1'); let child2 = new Child('le child 2'); // send second message from main notifications.add('yolo 2 from main'); } } |
Infine eseguiamo quando il DOM della nostra pagina web è stato caricato:
document.addEventListener("DOMContentLoaded", (e) => new Main()); |
Eseguiamo
Quando webPack esegue il tutto nel browser nel document.body
ci ritroveremo:
<p>1 - yolo 1 from main</p> <p>2 - yolo from le child 1</p> <p>3 - yolo from le child 2</p> <p>4 - yolo 2 from main</p> |
E’ proprio la numerazione a fianco che ci dimostra come l’istanza della classe “Notifications” sia unica.
Estendere la classe
// transformer.js import { Yolo } from "./yolo.js"; // Yolo is Yolo class class TransformerYolo extends Yolo {} export let transformerYolo = new TransformerYolo(); |
Esportare la classe “rompe” il pattern di singleton?!? Ebbene sì:
Singleton pattern restricts object creation for a class to only one instance.
Quindi se fate: import { Notifications, notifications } from "./notifications.js“;
e poi new Notifications();
, otterrete un’altra istanza della classe Notifications. Non si hanno restrizioni dal creare altre istanze della classe. Se invece si vuol ottenere il pattern singleton vero e proprio allora vi conviene NON esportare la classe. 🙂
Potete trovare un l’esempio funzionante descritto sopra su webpackbin.com
Commenti