Le singleton pattern : une classe à part

Les « patrons de conception », également appelés design patterns en anglais, aident les développeurs dans la programmation orientée objet avec des patrons éprouvés pour résoudre les tâches de programmation. Une fois qu’un modèle approprié a été trouvé parmi les quelques soixante-dix patrons de conception, il est adapté à chaque cas individuel. Toutefois, l’approche générale du patron reste la même. Le patron de conception Singleton est très puissant, mais il est considéré comme une relique de la programmation orientée objet. Vous apprendrez dans notre guide ce qu’il peut néanmoins faire, où et comment l’utiliser.

Qu’est-ce que le singleton pattern ?

Le singleton pattern appartient à la catégorie des patrons de création. Son but est d’éviter qu’une classe ne crée plus d’un objet. Pour ce faire, on crée l’objet souhaité dans une classe propre, puis on le récupère sous forme d’instance statique. Le singleton est l’un des patrons les plus simples, mais les plus puissants dans le développement de logiciels.

Citation

Le « Gang of Four » (GfO), une équipe de programmeurs des États-Unis, à propos du singleton pattern : « S’assurer qu’une classe ne dispose que d’une unique instance, et fournir un point d’accès global à celle-ci. »]

Quelles sont les propriétés du singleton pattern ?

Une fois que l’instance d’une classe a été créée à l’aide du patron de conception Singleton, il garantit qu’elle ne reste réellement qu’avec cette seule instance. Le singleton rend cette classe globalement accessible dans le logiciel. Il existe différentes méthodes pour cela dans les langages de programmation. Pour s’assurer qu’il reste avec une seule instance unique, il faut empêcher les utilisateurs de créer de nouvelles instances. Pour ce faire, le constructeur doit déclarer le patron comme « privé ». De cette façon, seul le code du Singleton peut instancier le Singleton lui-même. Cela garantit qu’un seul et même objet peut atteindre l’utilisateur. Si cette instance existe déjà, aucune nouvelle n’est créée. Voici un exemple de singleton possible :

public class Singleton {
	private static Singleton instance; // protégé contre l’accès externe et statique
	private Singleton() {} // constructeur privé avec protection d’accès externe
	public static Singleton getInstance() { // méthode publique, appel par code
		if (instance == nul) { // seulement si aucune instance n’existe, en créer une nouvelle
			instance = new Singleton();
		}
		return instance;
	}
}

Singleton pattern : représentation UML

En langage de modélisation unifié, en bref UML (Unified Modeling Language), le singleton complet est constitué d’un seul objet, car il n’y a qu’une seule instance d’une classe à créer.

Il n’est pas possible de changer quoi que ce soit de l’extérieur dans le singleton créé. C’est exactement le but de l’utilisation du singleton Pattern.

Avantages et inconvénients du patron de conception singleton

Coup d’œil sur les avantages

Un singleton peut être écrit rapidement et facilement car il n’est pas peuplé d’innombrables variables (globales). Il englobe sa création et peut donc également exercer un contrôle précis sur le moment et la manière dont on y accède. Un singleton pattern existant peut être dérivé en utilisant des sous-classes pour remplir de nouvelles fonctionnalités. L’utilisation qui en est faite est décidée de manière dynamique. Enfin, un singleton est créé exactement au moment où il est nécessaire, cette fonction est appelée Lazy Loading. Le processus d’instanciation d’un singleton avant qu’il ne soit nécessaire s’appelle Eager Loading.

Coup d’œil sur les inconvénients

L’utilisation effrénée du singleton conduit dans les faits à un état similaire à la programmation procédurale (c’est-à-dire non orientée objet) et peut conduire à un code source défectueux. La disponibilité globale de patrons de conception singleton présente des dangers lorsque des données sensibles sont traitées avec eux. Si des modifications sont apportées au singleton, il n’est pas possible de retracer les parties du programme qui sont affectées. Cela rend la maintenance des logiciels plus difficile car les dysfonctionnements ne peuvent être repérés qu’avec difficulté. La disponibilité globale rend également difficile la suppression du singleton, puisque les composants d’un logiciel peuvent toujours s’y référer. Dans les applications comportant de nombreux utilisateurs (applications multi-utilisateurs), un seul singleton peut réduire les performances, car il représente un goulot d’étranglement pour les données.

Le patron de conception singleton dans « la vraie vie »

Le singleton est généralement utilisé lorsque des tâches récurrentes doivent être effectuées dans le cadre d’une routine de programme. Il s’agit notamment des données à écrire dans un fichier, par exemple pour des fichiers logs ou des travaux d’impression qui doivent être écrits de manière répétée dans une seule mémoire tampon d’imprimante. Comme les pilotes et les mécanismes de cache ont toujours les mêmes processus, le singleton pattern est souvent utilisé là aussi.

Comme il est très difficile de tester le singleton pattern, nous illustrons ici son fonctionnement en prenant l’exemple du bureau d’une petite entreprise dans laquelle plusieurs employés utilisent une seule imprimante. Un exemple proche de la pratique est présenté dans la série de tutoriels de design pattern réalisé par Daniel H. Jacobsen. C’est sur cette base que le patron de conception Singleton suivant a été conçu :

Lorsqu’un utilisateur envoie une demande à l’imprimante, le singleton pose la « question » suivante : « Un objet Imprimante existe-t-il déjà ? Sinon, créez-en un. » Ce problème est résolu par une déclaration avec la condition if/then (return imprimante == null ?). Pour empêcher l’accès et donc les modifications, les variables individuelles et l’imprimante sont réglées sur « privé » et non « public ».

Public class Imprimante {
	private static Imprimante imprimante;
	private int nombre de pages;
	private imprimante() {
	}
	public static imprimante getInstance() {
		return imprimante== null ? 
				imprimante = nouvelle imprimante() : 
				imprimante ;
	}
	public void print(String text){
		System.out.println(text +
			"\n" + "nombre de pages imprimées aujourd’hui" + ++ nombrepages+
				"\n" + "---------") ;
	}
}

Ensuite, les employés de la branche sont « encapsulés ». Les chaînes de caractères des noms, de la position et de l’activité dans l’entreprise sont également réglées sur « privé ».

public class Employee {
	private final String nom;
	private final String position; 
	private final String activite;
	public Employe(String name, String position, String activite) {
		this.nom = nom;
		this.position = position;
		this.activite= activite;
	}
	public void printCurrent activite (){
		Imprimante imprimante = imprimante getInstance();
		imprimante.print("Employe: " + name+ "\n" +
			"Position: " + position + "\n" +.
			"Activite: " + activite + "\n");
	}
}

Enfin, les deux singletons sont inclus dans une routine de sortie.

Public class Main {
	public static void main(String[] args) {
		Employe andre = nouvel employe ("André",
				"patron",
				"Gère la branche") ;
		employee julie = new employee ("Julie",
				"conseillère" ,
				"conseille les clients en cas de réclamation");
		employe tom = nouvel employe ("Tom",
				"vendeur",
				" vend les produits");
		employee stephanie = new employee ("Stéphanie",
				"développeuse",
				"maintient l’informatique dans la succursale");
		employe matthieu = new employe ("Matthieu",
				"comptable",
				"Comptabilité financière de la succursale");
		andre.printCurrentactivite();
		julie.printCurrentactivite();
		tom.printCurrentactivite();
		stephanie.printCurrentactivite();
		matthieu.printCurrentactivite();
	}
}