Participer au site avec un Tip
Rechercher
 

Améliorations / Corrections

Vous avez des améliorations (ou des corrections) à proposer pour ce document : je vous remerçie par avance de m'en faire part, cela m'aide à améliorer le site.

Emplacement :

Description des améliorations :

Le design pattern « Singleton »

Qu'est-ce qu'un « Design Pattern » ? Le design pattern « Factory Method »


Accès rapide :
Présentation du design pattern Singleton
La représentation UML du Singleton
Mise en oeuvre du design pattern Singleton en Java
Approche « thread unsafe »
Approche « thread safe »
Prise en charge du pattern dans Eclipse
Définition d'un template Eclipse
Test du template singleton dans Eclipse

Présentation du design pattern Singleton

Le design pattern Singleton permet de s'assurer qu'une classe ne puisse produire qu'une seule et unique instance.

Sans aller chercher très loin, si l'on considère l'IDE Eclipse, il ne peut afficher qu'une seule et unique fenêtre principale par JVM. Or, il peut être utile de retrouver cette fenêtre de n'importe ou dans le programme Eclipse. La mise en oeuvre d'un desing pattern Singleton est donc tout indiquer pour ce besoin.

La représentation UML du Singleton

Voici une représentation générique du design pattern Singleton. Elle a été réalisée avec le modeleur UML Eclipse Papyrus.

Diagramme UML du design pattern Singleton
en UML, un membre de classe (attribut ou méthode) soulignés est un membre statique (porté par la classe et non par une instance).
en UML, un signe + symbolise la visibilité publique. Par opposition, le signe - symbolise la visibilité privée.

Comme vous le constater, on retire la possibilité de produire des instances de la classe en rendant le constructeur de la classe privé. Pour récupérer notre unique instance, nous utiliserons la méthode statique getInstance, qui elle pourra éventuellement invoquer le constructeur. Pour maintenir l'unique instance en mémoire, nous utiliserons l'attribut instance qui sera statique et privé.

Il faut bien comprendre que le design pattern Singleton ne traite que de l'unicité de l'instance. Pour autant, vous pouvez ensuite rajouter tout ce que vous souhaitez (attributs, méthodes...) à votre classe, en fonction de vos besoins.

Mise en oeuvre du design pattern Singleton en Java

Plusieurs « Implementation Patterns » Java peuvent être considérés pour implémenter le design pattern Singleton. La principale différences entre ces implémentations réside dans l'aspect « thread safe », ou non.

on qualifie un code de « thread safe », s'il peut être utilisé en simultané par plusieurs threads (plusieurs unités d'exécution) sans générer de problème particulier.

Approche « thread unsafe »

Une première approche naïve serait la suivante : c'est celle qu'on retrouve le plus souvent dans les exemples de singleton proposés sur Internet.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
package fr.koor.samples;

public final class Singleton {

    private static Singleton instance = null;

    private Singleton() {
    }

    public static Singleton getInstance() {
        if ( instance == null ) {
            instance = new Singleton();
        }
        return instance;
    }
    
}
Approche naïve du design pattern Singleton.
la classe est déclarée final, dit autrement on ne peut pas en dériver, car la construction d'objet est privatisée. Or, en cas d'héritage, un constructeur invoque obligatoirement son constructeur parent, ce qui ne serait pas possible ici.

Cette approche présente l'avantage de ne créer l'instance du singleton, que si elle est requise (si elle est demandée).

Mais cette approche présente aussi un inconvénient majeur : elle est non « thread safe ». Dans de très rares situations, plusieurs instances pourraient être produites si au moins deux threads demande à obtenir le singleton à des instants extrêmement proches. Vous ne devez donc considérer cette approche que et uniquement que si votre programme ne met pas en jeu de thread supplémentaire.

Et voici un petit exemple d'utilisation de notre classe de singleton.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
package fr.koor.samples;

public class Start {

    public static void main(String[] args) {
        
        Singleton ref1 = Singleton.getInstance();
        Singleton ref2 = Singleton.getInstance();
        
        System.out.println( ref1 );
        
        // On vérifie qu'on ait bien qu'une unique instance.
        System.out.println( ref1 == ref2 );
        
    }

}
Exemple d'utilisation de notre classe de singleton

Et voici les résultats produits par cet exemple.

fr.koor.samples.Singleton@6ff3c5b5
true

Approche « thread safe »

Deux variations peuvent être considérées pour implémenter un singleton thread safe. La première variation que je vous propose permet de ne produire le singleton que s'il est réellement utilisé. Mais du coup, elle est un peu plus complexe à écrire.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
 18 
 19 
 20 
 21 
package fr.koor.samples;
            
public final class Singleton {

    private static volatile Singleton instance = null;

    private Singleton() {
    }

    public static Singleton getInstance() {
        if ( instance == null ) {
            synchronized( Singleton.class ) {
                if ( instance == null ) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
    
}
l'instruction synchronized permet de garantir que son bloc d'instruction ne peut être exécuté que par un unique thread à la fois. Attention, la synchronisation est un mécanisme coûteux en temps : il faut donc faire cela que si c'est strictement nécessaire.
Le double if sur le test de l'instance n'est pas inutile. Le premier permet d'éviter, une fois le singleton produit, de synchroniser pour rien. Le second permet réellement de garantir qu'il n'y ait qu'une unique instance produite.

Une dernière solution consiste à produire l'instance dès le chargement de la classe en mémoire. Cette instanciation est garantie comme étant thread safe par l'environnement d'exécution Java : ça nous évite des lignes de code pour rien. Et cerise sur le gâteau, le code est plus performant car, par la suite, on ne teste plus la valeur null pour l'attribut statique !

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
package fr.koor.samples;
            
public final class Singleton {

    private static final Singleton INSTANCE = new Singleton();

    private Singleton() {
    }

    public static Singleton getInstance() {
        return INSTANCE;
    }
    
}
on marque l'instance du singleton comme étant final afin de garantir que personne puisse modifier cet attribut.
personnellement, c'est cette dernière approche que je privilégie la plupart du temps.

Prise en charge du pattern dans Eclipse

Définition d'un template Eclipse

Il est possible d'automatiser la génération du singleton dans Eclipse. Pour ce faire, il suffit de définir un nouveau « template » dans l'éditeur Java d'Eclipse. Réalisons cette manipulation ensemble.

Tout d'abord, commencez par ouvrir les préférences d'Eclipse : cliquez sur le menu « Window » puis sélectionnez « Preferences ». La boîte de dialogue des préférences doit s'ouvrir. Dans le panneau de gauche, sélectionnez le noeud « Java / Editor / Templates », comme le montre la capture d'écran ci-dessous.

Les templates de l'éditeur Java

Cliquez sur le bouton « New » en haut à droite pour créer un nouveau template. La boîte de dialogue suivante doit apparaître.

On créé un nouveau template

Appelez votre template « singleton » et assurez-vous qu'il soit lié au contexte Java. Vous pouvez lui donner une description, puis copier le code suivant dans la zone « Pattern ».

private static final ${primary_type_name} INSTANCE = new ${primary_type_name}();

private ${primary_type_name}() {
    // TODO : ${cursor}
}

public static ${primary_type_name} getInstance() {
    return INSTANCE;
}
Le code du template singleton
les séquences ${...} sont appelées de variables de template. On peut aussi les générer en cliquant sur le bouton « Insert Variable... ».

Test du template singleton dans Eclipse

Testons maintenant notre template. Pour ce faire, commencez par créer une nouvelle classe. Appelez-la Workbench (ou tout autre nom qui vous conviendra), puis placez-vous dans le corps de la classe et tapez les premières lettres du template suivies de la séquence de touches CTRL+Space. Un assistant vous proposera le template : si vous confirmez son utilisation, le code du singleton sera inséré dans votre classe.

Le template singleton vous est proposé
les variables utilisées dans le template Eclipse sont déjà remplacées par le nom de la classe dans l'aperçu proposé à droite.

Et voici le code finalement généré. Merci Eclipse ;-)

Le code de notre singleton est généré

Il ne reste plus qu'à compléter votre classe avec le code souhaité.



Qu'est-ce qu'un « Design Pattern » ? Le design pattern « Factory Method »