Le design pattern Singleton est parmi les plus courants en programmation orientée objet et quand on crée des applications en JavaScript, il peut manquer… Pour bien comprendre tout ce qui va suivre, vous aurez besoin de deux prérequis (si vous ne les avez pas, vous allez pouvoir les rattraper grâce à deux autres tutoriels de votre serviteur), à savoir les closures et plus particulièrement les variables statiques. À noter que je ne rappelle pas ici la façon dont on utilise la POO en JavaScript.

Quelle utilité ?

Avant de nous lancer dans l’implémentation du design pattern Singleton, il faudrait déjà répondre à une question : à quoi sert-il ? Prenons l’exemple d’un jeu vidéo. En POO, pour bien faire les choses, on a bien pris soin de séparer les différentes entités utiles au bon fonctionnement de notre jeu : une classe pour le moteur de jeu, une classe pour le moteur graphique, une classe s’occupant de stocker les images, etc. Seulement voila, la classe de stockage d’images, par exemple, sera probablement amenée à être utilisée plus d’une fois dans le jeu.

Problème : vous créerez un nouvel objet à chaque utilisation, ce qui fait que les images précédemment stockées ne le seront plus… Embêtant. Une solution possible serait d’utiliser les variables globales, mais si vous avez de bons réflexes alors vous vous direz « quoi de plus dégueulasse ? » et vous aurez raison ! Alors quoi, on crée une instance que l’on passe en argument, de fonction en fonction ? Vous vous en doutez bien, évidemment que non, ce n’est absolument pas pratique et nous savons tous qu’un programmeur est avant tout un gros fainéant !

D’où l’intérêt du Singleton. Une classe Singleton est une classe qui possède une instance unique durant toute l’exécution du programme (d’où son nom). C’est une classe qui stocke sa propre instance et qui transmet cette instance quand on le lui demande : ça paraît assez fou dit comme ça, mais vous allez comprendre comment une telle chose est possible avec la suite.

Implémentation en JavaScript

Avant même de créer notre classe, il faut nous demander comment nous allons stocker son instance. Elle sera stockée à l’intérieur de notre classe et, bien évidemment, elle devra toujours avoir son état précédent quand on la récupère, sans que ce soit un nouvel objet. Pour réaliser ça, il nous faut bien sûr une variable statique, et c’est pour ça qu’il fallait réfléchir au stockage avant la création de la classe car, comme vous le savez, une variable statique en JavaScript nécessite un peu de changement… Voici donc notre classe :

var Singleton = (function() {
	var instance = null;
})();

Bien sûr, vous choisissez le nom que vous voulez, Singleton n’est qu’un exemple. Avant toute chose, il nous faut créer le constructeur de notre classe, là où nous allons définir toutes ses méthodes et propriétés (non statiques) :

var Singleton = (function() {
	var constructeur = function() {
		this.methodePublique = function() {
		}
		
		var methodePrivee = function() {
		}
		
		var prop1, prop2;
	}
	
	var instance = null;
})();

Comprenez bien que tout ce qui se situe en-dehors du constructeur est statique donc définissez tout ce qui compose votre classe à l’intérieur du constructeur.

Nous avons donc notre classe mais aucun moyen d’y accéder… Ce qui est plutôt fâcheux donc nous allons y remédier tout de suite en créant un accesseur qui nous renverra notre instance. Si vous avez remarqué, nous ne stockons en fait rien du tout ici dans notre variable Singleton, il faut donc commencer par renvoyer quelque chose, et ce quelque chose ce sera un objet :

var Singleton = (function() {
	var constructeur = function() {
		this.methodePublique = function() {
		}
		
		var methodePrivee = function() {
		}
		
		var prop1, prop2;
	}
	
	var instance = null;
	return new function() {
	}
})();

On arrive au bout ! L’accesseur sera en fait une propriété publique de cet objet que l’on renvoie. Dans cet accesseur, on ne trouvera pas seulement une ligne s’occupant de retourner notre instance, pour la raison évidente suivante : si on ne fait que ça, l’instance n’est jamais créée ! Il faut donc avant tout insérer une condition afin de voir si l’instance a été créée (c’est facile : si elle n’a jamais été créée, la variable contenant notre instance vaut null). Si elle n’a pas été créée alors on s’en occupe.

var Singleton = (function() {
	var constructeur = function() {
		this.methodePublique = function() {
		}
		
		var methodePrivee = function() {
		}
		
		var prop1, prop2;
	}
	
	var instance = null;
	return new function() {
		this.getInstance = function() {
			if (instance == null) {
				instance = new constructeur();
				instance.constructeur = null;
			}
			
			return instance;
		}
	}
})();

Et le tour est joué ! Vous noterez une petite subtilité en plus : l’attribution de la valeur null à instance.constructeur. Cette affectation permet simplement d’être sûr que l’on ne pourra pas recréer un nouvel objet Singleton, en détruisant le constructeur.

Comment utiliser tout ça ?

Cette structure sera la même pour tous vos singletons : tout ce que vous avez à faire, c’est définir toutes les méthodes et propriétés dans le constructeur, le reste ne doit pas bouger. En ce qui concerne la façon dont on accède à l’instance, c’est simple :

Singleton.getInstance().methodePublique();

À chaque fois, vous n’aurez qu’à utiliser Singleton.getInstance(). Cette écriture est assez lourde, aussi, si vous en avez besoin plusieurs fois dans une fonction, n’hésitez pas à stocker votre instance :

var s = Singleton.getInstance();

Le JavaScript est fait de telle sorte que la variable s pointera bien sur le même objet que Singleton.getInstance(). Aucune copie ne sera faite et toutes les modifications faites sur s seront également faites sur l’instance stockée dans la classe.

Une conclusion, parce qu’on ne va pas se quitter comme ça

Vous savez donc utiliser le design pattern Singleton. Si vous n’avez pas forcément tout compris, n’hésitez pas à relire ou à poser vos questions en commentaire.