Java - Singleton Class

Bonjour là-bas, futurs magiciens de Java ! Aujourd'hui, nous allons plonger dans un des concepts les plus fascinants de la programmation Java : la classe Singleton. Ne vous inquiétez pas si vous êtes nouveaux en programmation ; je vais vous guider pas à pas, comme j'ai fait pour des centaines d'étudiants au fil des ans. Alors, prenez votre boisson favorite, installez-vous confortablement, et embarquons ensemble dans cette aventure passionnante !

Java - Singleton Class

Qu'est-ce qu'une classe Singleton ?

Imaginez que vous êtes le gestionnaire d'un club très exclusif. Ce club est si exclusif qu'il ne peut y avoir qu'une seule instance dans le monde entier. C'est essentiellement ce qu'est une classe Singleton en Java - une classe qui permet de créeronly one instance d'elle-même.

Pourquoi utiliser un Singleton ?

Vous pourriez vous demander : "Pourquoi voudrions-nous nous limiter à une seule instance ?" Eh bien, il y a plusieurs raisons :

  1. Point d'accès global : Il fournit un point d'accès unique à une instance particulière, rendant facile la gestion de l'état global dans une application.
  2. Initialisation paresseuse : L'instance est créée uniquement lorsqu'elle est nécessaire, économisant ainsi des ressources.
  3. Sécurité dans les threads : Lorsqu'elle est correctement implémentée, elle peut être thread-safe, permettant une seule instance même dans un environnement multi-threadé.

Maintenant, regardons comment nous pouvons créer une classe Singleton en Java.

Création d'une classe Singleton

Il y a plusieurs façons de créer une classe Singleton, mais nous allons commencer avec la méthode la plus simple et la plus courante : l'initialisation eager.

public class EagerSingleton {
// Instance privée statique de la classe
private static final EagerSingleton instance = new EagerSingleton();

// Constructeur privé pour empêcher l'instanciation
private EagerSingleton() {}

// Méthode publique pour retourner l'instance
public static EagerSingleton getInstance() {
return instance;
}
}

Reprenons cela :

  1. Nous déclarons une variable privée statique finale instance de type de la classe. C'est l'unique instance qui existera.
  2. Le constructeur est privé, empêchant les autres classes de créer de nouvelles instances.
  3. Nous fournissons une méthode publique statique getInstance() qui retourne l'unique instance.

Pour utiliser ce Singleton :

EagerSingleton singleton = EagerSingleton.getInstance();

Simple, n'est-ce pas ? Mais que faire si nous voulons créer l'instance uniquement lorsqu'elle est nécessaire ? C'est là que rentre en jeu l'initialisation paresseuse.

Initialisation Paresseuse

public class LazySingleton {
private static LazySingleton instance;

private LazySingleton() {}

public static LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}

Dans cette version, l'instance est créée uniquement lorsque getInstance() est appelée pour la première fois. Cependant, ce n'est pas thread-safe. Dans un environnement multi-threadé, nous pourrions finir par créer plusieurs instances. Corrigons cela !

Singleton Thread-Safe

public class ThreadSafeSingleton {
private static ThreadSafeSingleton instance;

private ThreadSafeSingleton() {}

public static synchronized ThreadSafeSingleton getInstance() {
if (instance == null) {
instance = new ThreadSafeSingleton();
}
return instance;
}
}

En ajoutant le mot-clé synchronized à la méthode getInstance(), nous nous assurons que seuls un thread peut exécuter cette méthode à la fois. Cependant, la synchronisation est coûteuse, et nous n'en avons besoin que la première fois que l'instance est créée. C'est là que rentre en jeu le patron double-checked locking !

Double-Checked Locking

public class DoubleCheckedSingleton {
private static volatile DoubleCheckedSingleton instance;

private DoubleCheckedSingleton() {}

public static DoubleCheckedSingleton getInstance() {
if (instance == null) {
synchronized (DoubleCheckedSingleton.class) {
if (instance == null) {
instance = new DoubleCheckedSingleton();
}
}
}
return instance;
}
}

Ce patron vérifie deux fois si instance est null : une fois sans verrouillage et une autre avec verrouillage. Le mot-clé volatile garantit que plusieurs threads gèrent correctement la variable instance.

Implémentation Singleton Bill Pugh

Maintenant, laissez-moi vous partager ma méthode favorite pour implémenter un Singleton, nommée d'après son créateur, Bill Pugh :

public class BillPughSingleton {
private BillPughSingleton() {}

private static class SingletonHelper {
private static final BillPughSingleton INSTANCE = new BillPughSingleton();
}

public static BillPughSingleton getInstance() {
return SingletonHelper.INSTANCE;
}
}

Cette approche utilise une classe interne statique pour contenir l'instance. Elle est thread-safe sans utiliser de synchronisation et charge l'instance paresseusement lorsque getInstance() est appelé pour la première fois.

Quand utiliser le Singleton ?

Les Singletons sont parfaits pour :

  1. Gérer une ressource partagée (comme une connexion à une base de données)
  2. Coordonner des actions à l'échelle du système
  3. Gérer un pool de ressources (comme des pools de threads)

Cependant, soyez prudents ! L'utilisation excessive de Singletons peut rendre votre code plus difficile à tester et à entretenir.

Méthodes Singleton

Voici un tableau des méthodes courantes que vous pourriez trouver dans une classe Singleton :

Méthode Description
getInstance() Retourne l'unique instance de la classe
readResolve() Utilisé pour la sérialisation pour préserver la propriété Singleton
clone() Généralement jette CloneNotSupportedException pour empêcher le clonage

Conclusion

Whaou ! Nous avons couvert beaucoup de terrain aujourd'hui. De la compréhension de ce qu'est un Singleton à l'implémentation de divers types de Singletons, vous êtes maintenant bien équipés pour utiliser ce puissant patron de conception dans vos projets Java.

Souvenez-vous, comme ce club exclusif dont nous avons parlé au début, un Singleton devrait être utilisé avec discernement. C'est un outil puissant, mais avec un grand pouvoir vient une grande responsabilité !

Alors que vous continuez votre voyage en Java, vous rencontrerez de nombreux autres concepts fascinants. Continuez à coder, continuez à apprendre, et surtout, amusez-vous ! Qui sait ? Peut-être que vous enseignerez un jour à la prochaine génération de programmeurs à propos des Singletons et bien plus encore.

Jusqu'à la prochaine fois, bon codage !

Credits: Image by storyset