web-dev-qa-db-fra.com

Quel est un moyen efficace d'implémenter un motif singleton en Java?

Quel est un moyen efficace d'implémenter un motif singleton en Java?

752

Utilisez un enum:

public enum Foo {
    INSTANCE;
}

Joshua Bloch a expliqué cette approche dans son Effective Java Reloaded à Google 2008: lien vers la vidéo . Voir également les diapositives 30 à 32 de sa présentation ( effective_Java_reloaded.pdf ):

La bonne façon de mettre en œuvre un singleton sérialisable

public enum Elvis {
    INSTANCE;
    private final String[] favoriteSongs =
        { "Hound Dog", "Heartbreak Hotel" };
    public void printFavorites() {
        System.out.println(Arrays.toString(favoriteSongs));
    }
}

Edit: An partie en ligne de "Effective Java" dit: 

"Cette approche est fonctionnellement équivalente à l'approche de champ public, à l'exception du fait qu'elle est plus concise, fournit le mécanisme de sérialisation gratuitement et offre une garantie absolue contre toute instanciation, même face à des attaques de sérialisation ou de réflexion sophistiquées. Pour être encore largement adopté, le type un seul élément est la meilleure façon de mettre en œuvre un singleton. "

743
Stephen Denne

Selon l'utilisation, il existe plusieurs réponses "correctes".

Depuis Java5, la meilleure façon de le faire est d’utiliser une énumération:

public enum Foo {
   INSTANCE;
}

Avant Java5, le cas le plus simple est:

public final class Foo {

    private static final Foo INSTANCE = new Foo();

    private Foo() {
        if (INSTANCE != null) {
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        return INSTANCE;
    }

    public Object clone() throws CloneNotSupportedException{
        throw new CloneNotSupportedException("Cannot clone instance of this class");
    }
}

Passons en revue le code. Premièrement, vous voulez que la classe soit finale. Dans ce cas, j'ai utilisé le mot clé final pour informer les utilisateurs qu'il est définitif. Ensuite, vous devez rendre le constructeur privé pour empêcher les utilisateurs de créer leur propre Foo. Le lancement d'une exception à partir du constructeur empêche les utilisateurs d'utiliser la réflexion pour créer un deuxième Foo. Ensuite, vous créez un champ private static final Foo pour contenir la seule instance et une méthode public static Foo getInstance() pour le renvoyer. La spécification Java s'assure que le constructeur n'est appelé que lors de la première utilisation de la classe.

Lorsque vous avez un objet très volumineux ou un code de construction lourd ET que vous disposez également d'autres méthodes ou champs statiques accessibles pouvant être utilisés avant qu'une instance ne soit nécessaire, vous devez alors uniquement utiliser une initialisation différée.

Vous pouvez utiliser un private static class pour charger l'instance. Le code ressemblerait alors à ceci:

public final class Foo {

    private static class FooLoader {
        private static final Foo INSTANCE = new Foo();
    }

    private Foo() {
        if (FooLoader.INSTANCE != null) {
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        return FooLoader.INSTANCE;
    }
}

Étant donné que la ligne private static final Foo INSTANCE = new Foo(); n'est exécutée que lorsque la classe FooLoader est réellement utilisée, cette opération prend en charge l'instanciation paresseuse et garantit la sécurité des threads.

Lorsque vous souhaitez également pouvoir sérialiser votre objet, vous devez vous assurer que la désérialisation ne créera pas de copie.

public final class Foo implements Serializable {

    private static final long serialVersionUID = 1L;

    private static class FooLoader {
        private static final Foo INSTANCE = new Foo();
    }

    private Foo() {
        if (FooLoader.INSTANCE != null) {
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        return FooLoader.INSTANCE;
    }

    @SuppressWarnings("unused")
    private Foo readResolve() {
        return FooLoader.INSTANCE;
    }
}

La méthode readResolve() s'assurera que la seule instance sera renvoyée, même lorsque l'objet a été sérialisé dans une exécution précédente de votre programme.

224
Roel Spilker

_ {Disclaimer: Je viens de résumer toutes les réponses géniales et de l'écrire dans mes mots.


Lors de la mise en œuvre de Singleton, nous avons 2 options
1. Chargement paresseux
2. Chargement anticipé

Le chargement paresseux ajoute un peu de temps supplémentaire (beaucoup pour être honnête), utilisez-le uniquement lorsque vous avez un objet très volumineux ou un code de construction lourd ET que vous disposez également d'autres méthodes statiques accessibles ou de champs pouvant être utilisés avant qu'une instance ne soit nécessaire, puis seulement à ce moment-là. vous devez utiliser une initialisation lente. Sinon, choisir un chargement précoce est un bon choix.

Le moyen le plus simple de mettre en œuvre Singleton est 

public class Foo {

    // It will be our sole hero
    private static final Foo INSTANCE = new Foo();

    private Foo() {
        if (INSTANCE != null) {
            // SHOUT
            throw new IllegalStateException("Already instantiated");
        }
    }

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

Tout est bon sauf son singleton chargé au début. Essayons singleton chargé paresseux

class Foo {

    // Our now_null_but_going_to_be sole hero 
    private static Foo INSTANCE = null;

    private Foo() {
        if (INSTANCE != null) {
            // SHOUT  
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        // Creating only  when required.
        if (INSTANCE == null) {
            INSTANCE = new Foo();
        }
        return INSTANCE;
    }
}

Jusqu'ici tout va bien, mais notre héros ne survivra pas en se battant seul avec plusieurs fils diaboliques qui veulent de nombreux exemples de notre héros . Alors protégeons-le des multiples filets diaboliques

class Foo {

    private static Foo INSTANCE = null;

    // TODO Add private shouting constructor

    public static Foo getInstance() {
        // No more tension of threads
        synchronized (Foo.class) {
            if (INSTANCE == null) {
                INSTANCE = new Foo();
            }
        }
        return INSTANCE;
    }
}

mais ce n'est pas suffisant pour protéger notre héros, vraiment !!! C’est ce que nous pouvons/devons faire de mieux pour aider notre héros. 

class Foo {

    // Pay attention to volatile
    private static volatile Foo INSTANCE = null;

    // TODO Add private shouting constructor

    public static Foo getInstance() {
        if (INSTANCE == null) { // Check 1
            synchronized (Foo.class) {
                if (INSTANCE == null) { // Check 2
                    INSTANCE = new Foo();
                }
            }
        }
        return INSTANCE;
    }
}

C'est ce qu'on appelle "l'idiome de verrouillage à double vérification". Il est facile d’oublier la déclaration volatile et difficile de comprendre pourquoi elle est nécessaire.
Pour plus de détails: http://www.cs.umd.edu/~pugh/Java/memoryModel/DoubleCheckedLocking.html

Maintenant nous sommes sûrs du mauvais fil, mais qu'en est-il de la sérialisation cruelle? Nous devons nous assurer que même pendant la désérialisation, aucun nouvel objet n'est créé.

class Foo implements Serializable {

    private static final long serialVersionUID = 1L;

    private static volatile Foo INSTANCE = null;

    // Rest of the things are same as above

    // No more fear of serialization
    @SuppressWarnings("unused")
    private Object readResolve() {
        return INSTANCE;
    }
}

La méthode readResolve() s'assurera que la seule instance sera retournée, même si l'objet a été sérialisé dans une exécution précédente de notre programme.

Enfin, nous avons ajouté une protection suffisante contre les threads et la sérialisation, mais notre code est volumineux et laid. Permet à notre héros de se refaire

public final class Foo implements Serializable {

    private static final long serialVersionUID = 1L;

    // Wrapped in a inner static class so that loaded only when required
    private static class FooLoader {

        // And no more fear of threads
        private static final Foo INSTANCE = new Foo();
    }

    // TODO add private shouting construcor

    public static Foo getInstance() {
        return FooLoader.INSTANCE;
    }

    // Damn you serialization
    @SuppressWarnings("unused")
    private Foo readResolve() {
        return FooLoader.INSTANCE;
    }
}

Oui c'est notre même héros :)
Puisque la ligne private static final Foo INSTANCE = new Foo(); n'est exécutée que lorsque la classe FooLoader est réellement utilisée, cela prend en charge l'instanciation paresseuse, 

et est-il garanti d'être thread-safe.

Et nous sommes venus jusqu'ici, voici le meilleur moyen de réaliser tout ce que nous avons fait est le meilleur moyen possible 

 public enum Foo {
       INSTANCE;
   }

Qui en interne sera traité comme 

public class Foo {

    // It will be our sole hero
    private static final Foo INSTANCE = new Foo();
}

C'est ça, plus de peur de la sérialisation, des threads et du code laid. De plus, les singles ENUMS sont initialisés paresseusement}

Cette approche est fonctionnellement équivalente à l’approche du champ public, sauf qu'il est plus concis, fournit le mécanisme de sérialisation gratuitement, et fournit une garantie absolue contre plusieurs instanciation, même face à une sérialisation sophistiquée ou attaques de réflexion. Bien que cette approche n’ait pas encore été adoptée à grande échelle, un type enum à un seul élément est le meilleur moyen d'implémenter un singleton.

-Joshua Bloch dans "Effective Java" 

Vous avez maintenant compris pourquoi les ENUMS sont considérés comme le meilleur moyen de mettre en œuvre Singleton et merci de votre patience :)
Mis à jour le sur mon blog

127
xyz

La solution publiée par Stu Thompson est valable pour Java5.0 et les versions ultérieures. Mais je préférerais ne pas l'utiliser parce que je pense que c'est sujet aux erreurs.

Il est facile d’oublier la déclaration volatile et difficile de comprendre pourquoi elle est nécessaire. Sans le code volatile, ce code ne serait plus thread-safe en raison de la vérification anti-modèle à double vérification. Pour plus d'informations à ce sujet, reportez-vous au paragraphe 16.2.4 de Java Concurrency in Practice . En bref: ce modèle (antérieur à Java 5.0 ou sans instruction volatile) peut renvoyer une référence à l'objet Bar qui est (toujours) dans un état incorrect.

Ce modèle a été inventé pour l'optimisation des performances. Mais ce n’est vraiment plus un problème. Le code d’initialisation paresseux suivant est rapide et, plus important encore, plus facile à lire.

class Bar {
    private static class BarHolder {
        public static Bar bar = new Bar();
    }

    public static Bar getBar() {
        return BarHolder.bar;
    }
}
121
Benno Richters

Fil de sécurité dans Java 5+:

class Foo {
    private static volatile Bar bar = null;
    public static Bar getBar() {
        if (bar == null) {
            synchronized(Foo.class) {
                if (bar == null)
                    bar = new Bar(); 
            }
        }
        return bar;
    }
}

EDIT: Faites attention au modificateur volatile ici. :) C'est important car sans cela, les autres threads ne sont pas garantis par le JMM (modèle de mémoire Java) pour voir les modifications apportées à sa valeur. La synchronisation non s'en occupe - elle sérialise uniquement l'accès à ce bloc de code.

EDIT 2 : @ La réponse de Bno détaille l'approche recommandée par Bill Pugh (FindBugs) et peut être discutée mieux. Allez lire et votez sa réponse aussi.

94
Stu Thompson

Oubliez initialisation lente , c'est trop problématique. C'est la solution la plus simple:

public class A {    

    private static final A INSTANCE = new A();

    private A() {}

    public static A getInstance() {
        return INSTANCE;
    }
}
90
Jonathan

Assurez-vous que vous en avez vraiment besoin. Faites un google pour "singleton anti-pattern" pour voir quelques arguments contre. Je suppose qu’il n’ya rien de mal à cela en soi, mais qu’il s’agit simplement d’un mécanisme permettant d’exposer des ressources/données globales, assurez-vous que c’est la meilleure façon. En particulier, j'ai trouvé l'injection de dépendance plus utile, en particulier si vous utilisez également des tests unitaires, car DI vous permet d'utiliser des ressources simulées à des fins de test.

47
Neil Burroughs

N'oubliez pas que Singleton n'est qu'un singleton pour le chargeur de classe qui l'a chargé. Si vous utilisez plusieurs chargeurs (conteneurs), chacun POURRAIT avoir sa propre version du Singleton.

26
Javamann

Certaines réponses suggérant que DI est une alternative à l’utilisation de singletons me mystifient. ce sont des concepts sans rapport. Vous pouvez utiliser DI pour injecter des instances singleton ou non-singleton (par exemple, par thread). Au moins, cela est vrai si vous utilisez Spring 2.x, je ne peux pas parler pour d’autres cadres DI.

Donc, ma réponse au PO serait (dans tous les exemples de code les plus triviaux):

  1. Utilisez un framework DI comme Spring, puis
  2. Faites-en partie de votre configuration DI si vos dépendances sont des singletons, une portée de requête, une portée de session ou autre.

Cette approche vous donne une architecture Nice découplée (et donc flexible et testable) où l’utilisation d’un singleton est un détail d’implémentation facilement réversible (à condition que les singletons que vous utilisez soient threadsafe, bien sûr).

21
Andrew Swan

Voyez vraiment pourquoi vous avez besoin d’un singleton avant de l’écrire. Il existe un débat quasi religieux sur leur utilisation sur lequel vous pouvez facilement tomber si vous recherchez des singletons sur Google en Java.

Personnellement, j'essaie d'éviter les singletons aussi souvent que possible pour plusieurs raisons, dont la plupart peuvent être trouvées en cherchant des singletons sur Google. Je pense que très souvent, les singletons sont maltraités parce qu'ils sont faciles à comprendre par tout le monde. Ils sont utilisés comme un mécanisme permettant d'introduire des données "globales" dans une conception OO et parce qu'ils sont faciles à contourner les objets. la gestion du cycle de vie (ou vraiment réfléchir à la façon dont vous pouvez faire A de B). Regardez des choses comme Inversion of Control (IoC) ou Injection de dépendance (DI) pour un milieu de Nice.

Si vous en avez vraiment besoin, alors Wikipedia a un bon exemple d’implémentation correcte d’un singleton.

20
Aidos

Voici 3 approches différentes

1) Enum

/**
* Singleton pattern example using Java Enumj
*/
public enum EasySingleton{
    INSTANCE;
}

2) Double vérification de chargement/chargement paresseux

/**
* Singleton pattern example with Double checked Locking
*/
public class DoubleCheckedLockingSingleton{
     private static volatile DoubleCheckedLockingSingleton INSTANCE;

     private DoubleCheckedLockingSingleton(){}

     public static DoubleCheckedLockingSingleton getInstance(){
         if(INSTANCE == null){
            synchronized(DoubleCheckedLockingSingleton.class){
                //double checking Singleton instance
                if(INSTANCE == null){
                    INSTANCE = new DoubleCheckedLockingSingleton();
                }
            }
         }
         return INSTANCE;
     }
}

3) méthode d'usine statique

/**
* Singleton pattern example with static factory method
*/

public class Singleton{
    //initailzed during class loading
    private static final Singleton INSTANCE = new Singleton();

    //to prevent creating another instance of Singleton
    private Singleton(){}

    public static Singleton getSingleton(){
        return INSTANCE;
    }
}
16
Abhijit Gaikwad

J'utilise Spring Framework pour gérer mes singletons. Il n'impose pas le "singleton-ness" de la classe (ce que vous ne pouvez pas faire de toute façon si plusieurs chargeurs de classe sont impliqués), mais fournit un moyen très simple de créer et de configurer différentes fabriques pour la création de différents types d'objets.

13
Matt

Version 1:

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

Chargement paresseux, thread-safe avec blocage, faible performance à cause de synchronized.

Version 2:

public class MySingleton {
    private MySingleton() {}
    private static class MySingletonHolder {
        public final static MySingleton instance = new MySingleton();
    }
    public static MySingleton getInstance() {
        return MySingletonHolder.instance;
    }
}

Chargement paresseux, thread-safe avec non-blocage, haute performance.

11
coderz

Wikipedia a quelques exemples de singletons, également en Java. L'implémentation de Java 5 semble assez complète et est thread-safe (verrouillage vérifié double appliqué).

10
macbirdie

Si vous n'avez pas besoin de chargement paresseux, essayez simplement

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

    private Singleton() {}

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

    protected Object clone() {
        throw new CloneNotSupportedException();
    }
}

Si vous souhaitez un chargement paresseux et que votre Singleton soit thread-safe, essayez le modèle de double vérification. 

public class Singleton {
        private static Singleton instance = null;

        private Singleton() {}

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

        protected Object clone() {
            throw new CloneNotSupportedException();
        }
}

Comme le schéma de double vérification n’est pas garanti (en raison de problèmes avec les compilateurs, je ne sais pas plus à ce sujet.), Vous pouvez également essayer de synchroniser l’ensemble de la méthode getInstance ou créer un registre pour tous vos singletons. 

10
Aleksi Yrttiaho

Je dirais Enum Singleton 

Singleton utilisant enum en Java est généralement un moyen de déclarer enum singleton. Enum singleton peut contenir une variable d'instance et une méthode d'instance. Par souci de simplicité, notez également que si vous utilisez une méthode d'instance, vous devez garantir la sécurité des threads de cette méthode si elle affecte l'état de l'objet.

L'utilisation d'une énumération est très facile à mettre en œuvre et ne présente aucun inconvénient en ce qui concerne les objets sérialisables, qui doivent être contournés par d'autres moyens.

/**
* Singleton pattern example using Java Enum
*/
public enum Singleton {
        INSTANCE;
        public void execute (String arg) {
                //perform operation here
        }
}

Vous pouvez y accéder par Singleton.INSTANCE, beaucoup plus facilement que d’appeler la méthode getInstance() sur Singleton.

1.12 Sérialisation des constantes Enum

Les constantes Enum sont sérialisées différemment des objets sérialisables ou externalisables ordinaires. La forme sérialisée d'une constante enum consiste uniquement en son nom; les valeurs de champ de la constante ne sont pas présentes dans le formulaire. Pour sérialiser une constante enum, ObjectOutputStream écrit la valeur renvoyée par la méthode du nom de la constante enum. Pour désérialiser une constante d’énumération, ObjectInputStream lit le nom de la constante dans le flux; la constante désérialisée est ensuite obtenue en appelant la méthode Java.lang.Enum.valueOf, en passant le type enum de la constante avec le nom de la constante reçu en argument. A l'instar d'autres objets sérialisables ou externalisables, les constantes enum peuvent servir de cibles de références arrière apparaissant ultérieurement dans le flux de sérialisation.

Le processus de sérialisation des constantes enum ne peut pas être personnalisé: les méthodes writeObject, readObject, readObjectNoData, writeReplace et readResolve spécifiques à la classe définies par les types enum sont ignorées lors de la sérialisation et de la désérialisation. De même, toutes les déclarations de champs serialPersistentFields ou serialVersionUID sont également ignorées - tous les types enum ont une valeur fixe serialVersionUID of 0L. Il n'est pas nécessaire de documenter les champs et les données sérialisables pour les types énumérés, car le type de données envoyé ne varie pas.

Cité de la documentation Oracle

Un autre problème avec les singletons conventionnels est qu’une fois que vous avez implémenté l’interface Serializable, ils ne restent plus singleton, car la méthode readObject() renvoie toujours une nouvelle instance comme le constructeur en Java. Ceci peut être évité en utilisant readResolve() et en supprimant l'instance nouvellement créée en remplaçant par singleton comme ci-dessous 

 // readResolve to prevent another instance of Singleton
 private Object readResolve(){
     return INSTANCE;
 }

Cela peut devenir encore plus complexe si votre classe Singleton conserve son état, car vous devez la rendre transitoire. Cependant, avec Enum Singleton, la sérialisation est garantie par JVM.


Bonne lecture

  1. Motif Singleton
  2. Enums, singletons et désérialisation
  3. Double vérification du verrouillage et du motif Singleton
8
NullPoiиteя
There are 4 ways to create a singleton in Java.

1- eager initialization singleton

    public class Test{
        private static final Test test = new Test();
        private Test(){}
        public static Test getTest(){
            return test;
        }
    }

2- lazy initialization singleton (thread safe)

    public class Test {
         private static volatile Test test;
         private Test(){}
         public static Test getTest() {
            if(test == null) {
                synchronized(Test.class) {
                    if(test == null){test = new Test();
                }
            }
         }

        return test;
    }


3- Bill Pugh Singleton with Holder Pattern (Preferably the best one)

    public class Test {

        private Test(){}

        private static class TestHolder{
            private static final Test test = new Test();
        }

        public static Test getInstance(){
            return TestHolder.test;
        }
    }

4- enum singleton
      public enum MySingleton {
        INSTANCE;
    private MySingleton() {
        System.out.println("Here");
    }
}
7
Dheeraj Sachan

Voici comment implémenter une simple singleton:

public class Singleton {
    // It must be static and final to prevent later modification
    private static final Singleton INSTANCE = new Singleton();
    /** The constructor must be private to prevent external instantiation */ 
    private Singleton(){}
    /** The public static method allowing to get the instance */
    public static Singleton getInstance() {
        return INSTANCE;
    }
}

Voici comment créer correctement votre singleton:

public class Singleton {
    // The constructor must be private to prevent external instantiation   
    private Singleton(){}
    /** The public static method allowing to get the instance */
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
    /** 
     * The static inner class responsible for creating your instance only on demand,
     * because the static fields of a class are only initialized when the class
     * is explicitly called and a class initialization is synchronized such that only 
     * one thread can perform it, this rule is also applicable to inner static class
     * So here INSTANCE will be created only when SingletonHolder.INSTANCE 
     * will be called
     */
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
}
4
Nicolas Filotto

Vous avez besoin de double vérification idiome si vous devez charger la variable d'instance d'une classe paresseusement. Si vous devez charger une variable statique ou un singleton paresseusement, vous avez besoin de initilization sur le détenteur de la demande idiome. 

De plus, si le singleton doit être sériliazble, tous les autres champs doivent être transitoires et la méthode readResolve () doit être implémentée afin de maintenir l'invariant d'objet singleton. Sinon, chaque fois que l'objet est désérialisé, une nouvelle instance de l'objet sera créée. Ce que readResolve () fait, c’est remplacer le nouvel objet lu par readObject (), ce qui a forcé ce nouvel objet à être nettoyé car il n’ya aucune variable qui y réfère.

public static final INSTANCE == ....
private Object readResolve() {
  return INSTANCE; // original singleton instance.
} 
3
Onur

Différentes façons de créer un objet singleton:

  1. Selon Joshua Bloch - Enum serait le meilleur.

  2. vous pouvez également utiliser le verrouillage à double contrôle.

  3. Même la classe statique interne peut être utilisée.

3
Shailendra Singh

Enum singleton

Le moyen le plus simple d'implémenter un Singleton compatible avec les threads est d'utiliser un Enum.

public enum SingletonEnum {
  INSTANCE;
  public void doSomething(){
    System.out.println("This is a singleton");
  }
}

Ce code fonctionne depuis l’introduction d’Enum en Java 1.5.

Double contrôle du verrouillage

Si vous souhaitez coder un singleton «classique» fonctionnant dans un environnement multithread (à partir de Java 1.5), vous devez utiliser celui-ci.

public 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 ;
  }
}

Ce n'est pas thread-safe avant 1.5 parce que l'implémentation du mot-clé volatile était différente.

Chargement précoce Singleton (fonctionne même avant Java 1.5)

Cette implémentation instancie le singleton lorsque la classe est chargée et assure la sécurité des threads.

public class Singleton {

  private static final Singleton instance = new Singleton();

  private Singleton() {
  }

  public static Singleton getInstance() {
    return instance;
  }

  public void doSomething(){
    System.out.println("This is a singleton");
  }

}
3
Dan Moldovan

Pour JSE 5.0 et les versions ultérieures, utilisez l’approche Enum, sinon utilisez l’approche du détenteur unique singulier statique (approche de chargement paresseux décrite par Bill Pugh). La solution Latter est également thread-safe sans nécessiter de construction de langage spéciale (instable ou synchronisée).

2
raoadnan

Un autre argument souvent utilisé contre les singletons est leur problème de testabilité. Les singletons ne sont pas facilement moquables à des fins de test. Si cela s'avère être un problème, j'aime bien apporter la légère modification suivante:

public class SingletonImpl {

    private static SingletonImpl instance;

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

    public static void setInstance(SingletonImpl impl) {
        instance = impl;
    }

    public void a() {
        System.out.println("Default Method");
    }
}

La méthode setInstance ajoutée permet de définir une implémentation sur maquette de la classe singleton lors des tests:

public class SingletonMock extends SingletonImpl {

    @Override
    public void a() {
        System.out.println("Mock Method");
    }

}

Cela fonctionne également avec les approches d'initialisation précoce:

public class SingletonImpl {

    private static final SingletonImpl instance = new SingletonImpl();

    private static SingletonImpl alt;

    public static void setInstance(SingletonImpl inst) {
        alt = inst;
    }

    public static SingletonImpl getInstance() {
        if (alt != null) {
            return alt;
        }
        return instance;
    }

    public void a() {
        System.out.println("Default Method");
    }
}

public class SingletonMock extends SingletonImpl {

    @Override
    public void a() {
        System.out.println("Mock Method");
    }

}

Cela présente l’inconvénient d’exposer également cette fonctionnalité à l’application normale. Les autres développeurs travaillant sur ce code pourraient être tentés d’utiliser la méthode ‘setInstance ’pour modifier une fonction spécifique et modifier ainsi le comportement de l’application tout entière. Cette méthode devrait donc contenir au moins un bon avertissement dans javadoc.

Néanmoins, pour la possibilité de réaliser des tests sur maquette (si nécessaire), cette exposition au code peut constituer un prix acceptable.

2
user3792852

classe singleton la plus simple

public class Singleton {
  private static Singleton singleInstance = new Singleton();
  private Singleton() {}
  public static Singleton getSingleInstance() {
    return singleInstance;
  }
}
1
rohan kamat

Je pense toujours qu'après Java 1.5, enum est la meilleure implémentation singleton disponible, car elle garantit également que même dans les environnements multithreads, une seule instance est créée.

public enum Singleton{ INSTANCE; }

et vous avez terminé !!!

0
shikjohari

Regardez ce post.

Exemples de modèles de conception GoF dans les bibliothèques principales de Java

De la section "Singleton" de la meilleure réponse,

Singleton (reconnaissable par des méthodes de création renvoyant à chaque fois la même instance (généralement d'elle-même))

  • Java.lang.Runtime # getRuntime ()
  • Java.awt.Desktop # getDesktop ()
  • Java.lang.System # getSecurityManager ()

Vous pouvez également apprendre l'exemple de Singleton à partir des classes natives Java elles-mêmes.

0
phi

Le meilleur modèle de singleton que j'ai jamais vu utilise l'interface du fournisseur.

  • C'est générique et réutilisable
  • Il supporte l'initialisation paresseuse
  • Elle est uniquement synchronisée jusqu'à son initialisation, puis le fournisseur bloquant est remplacé par un fournisseur non bloquant.

Voir ci-dessous:

public class Singleton<T> implements Supplier<T> {

    private boolean initialized;
    private Supplier<T> singletonSupplier;

    public Singleton(T singletonValue) {
        this.singletonSupplier = () -> singletonValue;
    }

    public Singleton(Supplier<T> supplier) {
        this.singletonSupplier = () -> {
            // The initial supplier is temporary; it will be replaced after initialization
            synchronized (supplier) {
                if (!initialized) {
                    T singletonValue = supplier.get();
                    // Now that the singleton value has been initialized,
                    // replace the blocking supplier with a non-blocking supplier
                    singletonSupplier = () -> singletonValue;
                    initialized = true;
                }
                return singletonSupplier.get();
            }
        };
    }

    @Override
    public T get() {
        return singletonSupplier.get();
    }
}
0
user1024314