Recherche…


Introduction

Un singleton est une classe qui ne possède qu'une seule instance. Pour plus d'informations sur le modèle de conception Singleton, reportez-vous à la rubrique Singleton dans la balise Design Patterns .

Enum Singleton

Java SE 5
public enum Singleton {
    INSTANCE;

    public void execute (String arg) {
        // Perform operation here 
    }
}

Les énumérations ont des constructeurs privés, sont finales et fournissent des machines de sérialisation appropriées. Ils sont également très concis et paresseusement initialisés de manière sécurisée.

La JVM garantit que les valeurs enum ne seront pas instanciées plus d’une fois chacune, ce qui confère au modèle enum singleton une défense très forte contre les attaques par réflexion.

Ce que le modèle enum ne protège pas, ce sont les autres développeurs qui ajoutent physiquement plus d'éléments au code source. Par conséquent, si vous choisissez ce style d'implémentation pour vos singletons, il est impératif que vous décriviez très clairement qu'aucune nouvelle valeur ne devrait être ajoutée à ces énumérations.

C'est la manière recommandée d'implémenter le modèle singleton, comme expliqué par Joshua Bloch dans Effective Java.

Filetage sûr Singleton avec double verrouillage de vérification

Ce type de Singleton est thread-safe et empêche le verrouillage inutile après la création de l'instance Singleton.

Java SE 5
public class MySingleton {

    // instance of class
    private static volatile MySingleton instance = null;

    // Private constructor
    private MySingleton() {
        // Some code for constructing object
    }

    public static MySingleton getInstance() {
        MySingleton result = instance;
        
        //If the instance already exists, no locking is necessary
        if(result == null) {
            //The singleton instance doesn't exist, lock and check again
            synchronized(MySingleton.class) {
                result = instance;
                if(result == null) {
                    instance = result = new MySingleton();
                }
            }
        }
        return result;
    }
}

Il faut le souligner - dans les versions antérieures à Java SE 5, l'implémentation ci-dessus est incorrecte et doit être évitée. Il est impossible d’implémenter correctement le verrouillage à double vérification dans Java avant Java 5.

Singleton sans utilisation d'Enum (initialisation enthousiaste)

public class Singleton {    

    private static final Singleton INSTANCE = new Singleton();

    private Singleton() {}

    public static Singleton getInstance() {
        return INSTANCE;
    }
}

On peut soutenir que cet exemple est une initialisation paresseuse. La section 12.4.1 de la spécification du langage Java indique:

Une classe ou un type d'interface T sera initialisé immédiatement avant la première occurrence de l'un des éléments suivants:

  • T est une classe et une instance de T est créée
  • T est une classe et une méthode statique déclarée par T est invoquée
  • Un champ statique déclaré par T est affecté
  • Un champ statique déclaré par T est utilisé et le champ n'est pas une variable constante
  • T est une classe de niveau supérieur et une instruction assert lexicale imbriquée dans T est exécutée.

Par conséquent, tant qu'il n'y a pas d'autres champs statiques ou méthodes statiques dans la classe, l'instance Singleton ne sera pas initialisée tant que la méthode getInstance() n'est pas appelée pour la première fois.

Initialisation par défaut sécurisée des threads à l'aide de la classe holder | Mise en œuvre de Bill Pugh Singleton

public class Singleton {
    private static class InstanceHolder {
        static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance() {
        return InstanceHolder.INSTANCE;
    }

    private Singleton() {}
}

Cela initialise la variable INSTANCE lors du premier appel à Singleton.getInstance() , en tirant parti des garanties de sécurité des threads de la langue pour l'initialisation statique sans nécessiter de synchronisation supplémentaire.

Cette implémentation est également connue sous le nom de singleton de Bill Pugh. [Wiki]

Extension de singleton (héritage singleton)

Dans cet exemple, la classe de base Singleton fournit la méthode getMessage() qui renvoie "Hello world!" message.

Il est sous - classes UppercaseSingleton et LowercaseSingleton remplacer méthode getMessage () pour fournir une représentation appropriée du message.

//Yeah, we'll need reflection to pull this off.
import java.lang.reflect.*;

/*
Enumeration that represents possible classes of singleton instance.
If unknown, we'll go with base class - Singleton.
*/
enum SingletonKind {
    UNKNOWN,
    LOWERCASE,
    UPPERCASE
}

//Base class
class Singleton{

    /*
    Extended classes has to be private inner classes, to prevent extending them in 
    uncontrolled manner.
     */
    private class UppercaseSingleton extends Singleton {

        private UppercaseSingleton(){
            super();
        }

        @Override
        public String getMessage() {
            return super.getMessage().toUpperCase();
        }
    }

    //Another extended class.
    private class LowercaseSingleton extends Singleton
    {
        private LowercaseSingleton(){
            super();
        }

        @Override
        public String getMessage() {
            return super.getMessage().toLowerCase();
        }
    }

    //Applying Singleton pattern
    private static SingletonKind kind = SingletonKind.UNKNOWN;

    private static Singleton instance;

    /*
    By using this method prior to getInstance() method, you effectively change the
    type of singleton instance to be created.
     */
    public static void setKind(SingletonKind kind) {
        Singleton.kind = kind;
    }

    /*
    If needed, getInstance() creates instance appropriate class, based on value of
    singletonKind field.
     */
    public static Singleton getInstance() 
        throws  NoSuchMethodException, 
                IllegalAccessException, 
                InvocationTargetException, 
                InstantiationException {

        if(instance==null){
            synchronized (Singleton.class){
                if(instance==null){
                    Singleton singleton = new Singleton();
                    switch (kind){
                        case UNKNOWN:

                            instance = singleton;
                            break;

                        case LOWERCASE:

                            /*
                             I can't use simple

                             instance = new LowercaseSingleton();

                             because java compiler won't allow me to use
                             constructor of inner class in static context,
                             so I use reflection API instead.

                             To be able to access inner class by reflection API,
                             I have to create instance of outer class first.
                             Therefore, in this implementation, Singleton cannot be
                             abstract class.
                             */

                            //Get the constructor of inner class.
                            Constructor<LowercaseSingleton> lcConstructor =
                                    LowercaseSingleton.class.getDeclaredConstructor(Singleton.class);

                            //The constructor is private, so I have to make it accessible.
                            lcConstructor.setAccessible(true);

                            // Use the constructor to create instance.
                            instance = lcConstructor.newInstance(singleton);

                            break;

                        case UPPERCASE:

                            //Same goes here, just with different type
                            Constructor<UppercaseSingleton> ucConstructor =
                                    UppercaseSingleton.class.getDeclaredConstructor(Singleton.class);
                            ucConstructor.setAccessible(true);
                            instance = ucConstructor.newInstance(singleton);
                    }
                }
            }
        }
        return instance;
    }

    //Singletons state that is to be used by subclasses
    protected String message;

    //Private constructor prevents external instantiation.
    private Singleton()
    {
        message = "Hello world!";
    }

    //Singleton's API. Implementation can be overwritten by subclasses.
    public String getMessage() {
        return message;
    }
}

//Just a small test program
public class ExtendingSingletonExample {

    public static void main(String args[]){

        //just uncomment one of following lines to change singleton class

        //Singleton.setKind(SingletonKind.UPPERCASE);
        //Singleton.setKind(SingletonKind.LOWERCASE);

        Singleton singleton = null;
        try {
            singleton = Singleton.getInstance();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
        System.out.println(singleton.getMessage());
    }
}


Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow