Singleton avec des arguments en Java


142

Je lisais l'article Singleton sur Wikipedia et je suis tombé sur cet exemple:

public class Singleton {
    // Private constructor prevents instantiation from other classes
    private Singleton() {}

    /**
     * SingletonHolder is loaded on the first execution of Singleton.getInstance() 
     * or the first access to SingletonHolder.INSTANCE, not before.
     */
    private static class SingletonHolder { 
        private static final Singleton INSTANCE = new Singleton();
    }

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

Bien que j'aime vraiment la façon dont ce Singleton se comporte, je ne vois pas comment l'adapter pour incorporer des arguments au constructeur. Quelle est la meilleure façon de procéder en Java? Dois-je faire quelque chose comme ça?

public class Singleton
{
    private static Singleton singleton = null;  
    private final int x;

    private Singleton(int x) {
        this.x = x;
    }

    public synchronized static Singleton getInstance(int x) {
        if(singleton == null) singleton = new Singleton(x);
        return singleton;
    }
}

Merci!


Edit: Je pense que j'ai commencé une tempête de controverse avec mon désir d'utiliser Singleton. Laissez-moi vous expliquer ma motivation et j'espère que quelqu'un pourra suggérer une meilleure idée. J'utilise un cadre de calcul en grille pour exécuter des tâches en parallèle. En général, j'ai quelque chose comme ça:

// AbstractTask implements Serializable
public class Task extends AbstractTask
{
    private final ReferenceToReallyBigObject object;

    public Task(ReferenceToReallyBigObject object)
    {
        this.object = object;
    }

    public void run()
    {
        // Do some stuff with the object (which is immutable).
    }
}

Ce qui se passe, c'est que même si je transmets simplement une référence à mes données à toutes les tâches, lorsque les tâches sont sérialisées, les données sont copiées encore et encore. Ce que je veux faire, c'est partager l'objet entre toutes les tâches. Naturellement, je pourrais modifier la classe comme ceci:

// AbstractTask implements Serializable
public class Task extends AbstractTask
{
    private static ReferenceToReallyBigObject object = null;

    private final String filePath;

    public Task(String filePath)
    {
        this.filePath = filePath;
    }

    public void run()
    {
        synchronized(this)
        {
            if(object == null)
            {
                ObjectReader reader = new ObjectReader(filePath);
                object = reader.read();
            }
        }

        // Do some stuff with the object (which is immutable).
    }
}

Comme vous pouvez le voir, même ici, j'ai le problème que le passage d'un chemin de fichier différent ne signifie rien après le premier. C'est pourquoi j'aime l'idée d'un magasin qui a été affichée dans les réponses. Quoi qu'il en soit, plutôt que d'inclure la logique de chargement du fichier dans la méthode d'exécution, je voulais résumer cette logique dans une classe Singleton. Je ne donnerai pas encore un autre exemple, mais j'espère que vous comprenez l'idée. S'il vous plaît laissez-moi entendre vos idées pour une manière plus élégante d'accomplir ce que j'essaie de faire. Merci encore!


1
Le modèle d'usine est ce que vous voulez. Dans l'idéal, les tâches de grille doivent être complètement indépendantes de toute autre chose et recevoir toutes les données dont elles ont besoin pour exécuter et renvoyer leurs résultats. Cependant, ce n'est pas toujours la solution la plus réalisable, donc la sérialisation des données dans un fichier n'est pas une si mauvaise idée. Je pense que tout ce truc de singleton est un peu un hareng rouge; vous ne voulez pas de singleton.
oxbow_lakes

2
Il est assez malheureux que vous ayez utilisé le terme Singleton qui accompagne un tel bagage. Le terme approprié pour ce modèle est en fait Interning. Interning est une méthode pour garantir que les valeurs abstraites sont représentées par une seule instance. L'intégration de chaînes est l'utilisation la plus courante: en.wikipedia.org/wiki/String_intern_pool.
notnoop

Vous voudrez peut-être jeter un coup d'œil à Terracotta. Il maintient l'identité d'objet dans le cluster. Lorsque vous envoyez une référence à des données déjà présentes dans le cluster, elles ne sont pas re-sérialisées.
Taylor Gautier

21
En mettant de côté la question de savoir si le modèle de singleton doit jamais être utilisé, je noterais que presque toutes les réponses ici semblent supposer que le but de fournir un argument est de permettre la création de "plusieurs singletons" qui se distinguent par la valeur dudit paramètre. Mais un autre objectif possible est de fournir un accès à un objet externe qui est le seul objet du genre dont l' instance unique de la classe singleton aura besoin. Nous devons donc distinguer un paramètre fourni pour un tel accès d'un paramètre destiné à créer «plusieurs instances singleton».
Carl

2
Un autre scénario pour un "singleton avec paramètres": une application web qui construira son unique singleton immuable basé sur les informations fournies avec la toute première requête (thread) à venir. Le domaine de la requête pourrait déterminer le comportement de certains singleton par exemple
fustaki

Réponses:


171

Je vais clarifier mon propos: un singleton avec des paramètres n'est pas un singleton .

Un singleton, par définition, est un objet que vous ne souhaitez pas instancier plus d'une fois. Si vous essayez de fournir des paramètres au constructeur, quel est le point du singleton?

Vous avez deux options. Si vous voulez que votre singleton soit initialisé avec certaines données, vous pouvez le charger avec des données après instanciation , comme ceci:

SingletonObj singleton = SingletonObj.getInstance();
singleton.init(paramA, paramB); // init the object with data

Si l'opération que votre singleton est en train d'effectuer est récurrente, et avec des paramètres différents à chaque fois, vous pouvez aussi bien passer les paramètres à la méthode principale en cours d'exécution:

SingletonObj singleton = SingletonObj.getInstance();
singleton.doSomething(paramA, paramB); // pass parameters on execution

Dans tous les cas, l'instanciation sera toujours sans paramètre. Sinon, votre singleton n'est pas un singleton.


1
+1 C'est ainsi que je le ferais probablement lors du codage. En C #, j'utiliserais simplement des propriétés. Java, probablement comme ça.
Zack

131
désolé, ce n'est pas vrai. il existe des situations dans lesquelles vous devez transmettre des paramètres créés de manière dynamique qui restent les mêmes pour l'exécution de l'application de perçage. vous ne pouvez donc pas utiliser une constante dans le singleton mais devez passer cette constante lors de sa création. après avoir passé une fois, c'est la même constante pour le temps du trou. un setter ne fera pas le travail si vous avez besoin de cette constante spécifique dans le constructeur.
masi

1
@masi, comme le dit l'auteur - ce n'est pas un singleton. Si vous devez transmettre une dynamique constante, vous devrez peut-être créer de nombreuses classes de ce type avec des constantes différentes. Donc, il ne sert à rien de singleton.
Dmitry Zaytsev

53
Si vous n'avez besoin que d'une seule instance d'une classe pendant toute la durée de vie d'une application, mais que vous devez fournir à cette instance une valeur au moment du lancement, pourquoi n'est-ce plus un singleton?
Oscar

4
"Si vous essayez de fournir des paramètres au constructeur, quel est l'intérêt du singleton?" - On pourrait aussi dire: "Si vous créez une instance unique de toute votre application, à quoi servent les arguments de ligne de commande?", Et la réponse est que cela a beaucoup de sens. On pourrait maintenant dire que c'est assez différent d'une classe singleton, sauf si la classe est en fait la classe Main qui reçoit les args [] de la méthode main - alors c'est même la même chose. Le dernier argument, qui pourrait bien tenir, est qu'il s'agit d'une situation assez exceptionnelle.
Dreamspace President

41

Je pense que vous avez besoin de quelque chose comme une usine pour avoir des objets avec divers paramètres instanciés et réutilisés. Il peut être implémenté en utilisant un paramètre synchronisé HashMapou en ConcurrentHashMapmappant un paramètre ( Integerun exemple) à votre classe paramétrable «singleton».

Bien que vous puissiez en arriver au point où vous devriez utiliser à la place des classes régulières non singleton (par exemple nécessitant 10 000 singleton paramétrés différemment).

Voici un exemple pour un tel magasin:

public final class UsefulObjFactory {

    private static Map<Integer, UsefulObj> store =
        new HashMap<Integer, UsefulObj>();

    public static final class UsefulObj {
        private UsefulObj(int parameter) {
            // init
        }
        public void someUsefulMethod() {
            // some useful operation
        }
    }

    public static UsefulObj get(int parameter) {
        synchronized (store) {
            UsefulObj result = store.get(parameter);
            if (result == null) {
                result = new UsefulObj(parameter);
                store.put(parameter, result);
            }
            return result;
        }
    }
}

Pour pousser encore plus loin, les Java enumpeuvent également être considérés (ou utilisés comme) des singletons paramétrés, bien que n'autorisant qu'un nombre fixe de variantes statiques.

Cependant, si vous avez besoin d'une solution distribuée 1 , envisagez une solution de mise en cache latérale. Par exemple: EHCache, Terracotta, etc.

1 dans le sens de couvrir plusieurs machines virtuelles sur probablement plusieurs ordinateurs.


Oui, c'est exactement ce dont j'ai besoin. Merci beaucoup! Je conviens que la façon dont je traitais les arguments dans mon exemple n'avait pas beaucoup de sens, mais je n'y ai pas pensé. Voir mon explication dans les commentaires de la réponse d'oxbow_lakes.

1
Ce n'est PAS un singleton; vous en avez maintenant plus d'un. LOL
oxbow_lakes

@Scott: Je suggérerais quelque chose comme ce que Yuval a suggéré ci-dessous. Cela a un peu plus de sens et vous avez un «vrai» singleton. edit
Zack

J'espère que personne ne me dérange de modifier les noms dans le code; J'imagine que c'est vraiment déroutant pour les débutants.
Revenir en arrière

Oui, nous pourrions les appeler Multitron et toujours atteindre le même objectif que l'OP voulait en premier lieu à mon humble avis.
akarnokd le

22

Vous pouvez ajouter une méthode d'initialisation configurable afin de séparer l'instanciation de l'obtention.

public class Singleton {
    private static Singleton singleton = null;
    private final int x;

    private Singleton(int x) {
        this.x = x;
    }

    public static Singleton getInstance() {
        if(singleton == null) {
            throw new AssertionError("You have to call init first");
        }

        return singleton;
    }

    public synchronized static Singleton init(int x) {
        if (singleton != null)
        {
            // in my opinion this is optional, but for the purists it ensures
            // that you only ever get the same instance when you call getInstance
            throw new AssertionError("You already initialized me");
        }

        singleton = new Singleton(x);
        return singleton;
    }

}

Ensuite, vous pouvez appeler Singleton.init(123)une fois pour le configurer, par exemple au démarrage de votre application.


13

Vous pouvez également utiliser le modèle Builder si vous souhaitez montrer que certains paramètres sont obligatoires.

    public enum EnumSingleton {

    INSTANCE;

    private String name; // Mandatory
    private Double age = null; // Not Mandatory

    private void build(SingletonBuilder builder) {
        this.name = builder.name;
        this.age = builder.age;
    }

    // Static getter
    public static EnumSingleton getSingleton() {
        return INSTANCE;
    }

    public void print() {
        System.out.println("Name "+name + ", age: "+age);
    }


    public static class SingletonBuilder {

        private final String name; // Mandatory
        private Double age = null; // Not Mandatory

        private SingletonBuilder(){
          name = null;
        }

        SingletonBuilder(String name) {
            this.name = name;
        }

        public SingletonBuilder age(double age) {
            this.age = age;
            return this;
        }

        public void build(){
            EnumSingleton.INSTANCE.build(this);
        }

    }


}

Ensuite, vous pouvez le créer / instancier / paramétrer comme suit:

public static void main(String[] args) {
    new EnumSingleton.SingletonBuilder("nico").age(41).build();
    EnumSingleton.getSingleton().print();
}

6

L' instruction " Un singleton avec des paramètres n'est pas un singleton " n'est pas complètement correcte . Nous devons analyser cela du point de vue de l'application plutôt que du point de vue du code.

Nous construisons une classe singleton pour créer une seule instance d'un objet en une seule exécution d'application. En ayant un constructeur avec paramètre, vous pouvez intégrer de la flexibilité dans votre code pour modifier certains attributs de votre objet singleton à chaque fois que vous exécutez votre application. Ce n'est pas une violation du modèle Singleton. Cela ressemble à une violation si vous voyez cela du point de vue du code.

Les Design Patterns sont là pour nous aider à écrire du code flexible et extensible, pas pour nous empêcher d'écrire un bon code.


12
Ce n'est pas une réponse à la question du PO, cela devrait être un commentaire.
Thierry J.

5

Utilisez des getters et des setters pour définir la variable et rendre le constructeur par défaut privé. Puis utilisez:

Singleton.getInstance().setX(value);

1
Ne comprenez pas pourquoi cela a été voté à la baisse. C'est une réponse valable. : /
Zack

13
Parce que c'est une mauvaise réponse. Par exemple, imaginez un système dans lequel le nom d'utilisateur et le mot de passe initiaux de l'administrateur initial sont des arguments de constructeur. Maintenant, si je fais de cela un singleton et que je fais ce que vous dites, je reçois des getters et des setters pour l'administrateur, ce qui n'est pas tout à fait ce que vous voulez. Donc, bien que votre option puisse être valide dans certains cas, elle ne répond pas vraiment au cas général qui était la question. (oui, je travaille sur le système que j'ai décrit et non, je n'aurais pas utilisé un modèle singleton si ce n'était pas pour le fait que le devoir dit "utiliser un modèle singleton ici")
Jasper

5

Surpris que personne n'ait mentionné comment un enregistreur est créé / récupéré. Par exemple, ci-dessous montre comment l' enregistreur Log4J est récupéré.

// Retrieve a logger named according to the value of the name parameter. If the named logger already exists, then the existing instance will be returned. Otherwise, a new instance is created.
public static Logger getLogger(String name)

Il y a quelques niveaux d'indirections, mais la partie clé est la méthode ci-dessous qui dit à peu près tout sur son fonctionnement. Il utilise une table de hachage pour stocker les enregistreurs sortants et la clé est dérivée du nom. Si l'enregistreur n'existe pas pour un nom donné, il utilise une fabrique pour créer l'enregistreur, puis l'ajoute à la table de hachage.

69   Hashtable ht;
...
258  public
259  Logger getLogger(String name, LoggerFactory factory) {
260    //System.out.println("getInstance("+name+") called.");
261    CategoryKey key = new CategoryKey(name);
262    // Synchronize to prevent write conflicts. Read conflicts (in
263    // getChainedLevel method) are possible only if variable
264    // assignments are non-atomic.
265    Logger logger;
266
267    synchronized(ht) {
268      Object o = ht.get(key);
269      if(o == null) {
270        logger = factory.makeNewLoggerInstance(name);
271        logger.setHierarchy(this);
272        ht.put(key, logger);
273        updateParents(logger);
274        return logger;
275      } else if(o instanceof Logger) {
276        return (Logger) o;
277      } 
...

4

Modification du modèle Singleton qui utilise l'initialisation de Bill Pugh sur l'idiome du détenteur de la demande . Ceci est thread-safe sans la surcharge des constructions de langage spécialisées (c'est-à-dire volatiles ou synchronisées):

public final class RInterfaceHL {

    /**
     * Private constructor prevents instantiation from other classes.
     */
    private RInterfaceHL() { }

    /**
     * R REPL (read-evaluate-parse loop) handler.
     */
    private static RMainLoopCallbacks rloopHandler = null;

    /**
     * SingletonHolder is loaded, and the static initializer executed, 
     * on the first execution of Singleton.getInstance() or the first 
     * access to SingletonHolder.INSTANCE, not before.
     */
    private static final class SingletonHolder {

        /**
         * Singleton instance, with static initializer.
         */
        private static final RInterfaceHL INSTANCE = initRInterfaceHL();

        /**
         * Initialize RInterfaceHL singleton instance using rLoopHandler from
         * outer class.
         * 
         * @return RInterfaceHL instance
         */
        private static RInterfaceHL initRInterfaceHL() {
            try {
                return new RInterfaceHL(rloopHandler);
            } catch (REngineException e) {
                // a static initializer cannot throw exceptions
                // but it can throw an ExceptionInInitializerError
                throw new ExceptionInInitializerError(e);
            }
        }

        /**
         * Prevent instantiation.
         */
        private SingletonHolder() {
        }

        /**
         * Get singleton RInterfaceHL.
         * 
         * @return RInterfaceHL singleton.
         */
        public static RInterfaceHL getInstance() {
            return SingletonHolder.INSTANCE;
        }

    }

    /**
     * Return the singleton instance of RInterfaceHL. Only the first call to
     * this will establish the rloopHandler.
     * 
     * @param rloopHandler
     *            R REPL handler supplied by client.
     * @return RInterfaceHL singleton instance
     * @throws REngineException
     *             if REngine cannot be created
     */
    public static RInterfaceHL getInstance(RMainLoopCallbacks rloopHandler)
            throws REngineException {
        RInterfaceHL.rloopHandler = rloopHandler;

        RInterfaceHL instance = null;

        try {
            instance = SingletonHolder.getInstance();
        } catch (ExceptionInInitializerError e) {

            // rethrow exception that occurred in the initializer
            // so our caller can deal with it
            Throwable exceptionInInit = e.getCause();
            throw new REngineException(null, exceptionInInit.getMessage());
        }

        return instance;
    }

    /**
     * org.rosuda.REngine.REngine high level R interface.
     */
    private REngine rosudaEngine = null;

    /**
     * Construct new RInterfaceHL. Only ever gets called once by
     * {@link SingletonHolder.initRInterfaceHL}.
     * 
     * @param rloopHandler
     *            R REPL handler supplied by client.
     * @throws REngineException
     *             if R cannot be loaded.
     */
    private RInterfaceHL(RMainLoopCallbacks rloopHandler)
            throws REngineException {

        // tell Rengine code not to die if it can't
        // load the JRI native DLLs. This allows
        // us to catch the UnsatisfiedLinkError
        // ourselves
        System.setProperty("jri.ignore.ule", "yes");

        rosudaEngine = new JRIEngine(new String[] { "--no-save" }, rloopHandler);
    }
}

Je pense que ce serait - il une bonne idée finally { RInterfaceHL.rloopHandler = null; }en getInstance, parce que référence statique peut provoquer une fuite de mémoire si nous ne faisons pas attention. Dans votre cas, il semble que ce n'est pas un problème, mais je pourrais imaginer un scénario où l'objet passé est gros et utilisé uniquement par RInterfaceHLctor pour obtenir des valeurs, et non pour en garder une référence.
TWiStErRob

Idée: return SingletonHolder.INSTANCEfonctionnerait aussi bien dans getInstance. Je ne pense pas qu'il y ait un besoin d'encapsulation ici, parce que la classe externe connaît déjà les entrailles de la classe interne, elles sont étroitement liées: elle sait qu'il a rloopHandlerbesoin de init avant d'appeler. De plus, le constructeur privé n'a aucun effet, car les éléments privés de la classe interne sont simplement disponibles pour la classe externe.
TWiStErRob

1
Le lien est rompu. Faites-vous référence à en.wikipedia.org/wiki/Initialization-on-demand_holder_idiom ?
Jorge Lavín

3

La raison pour laquelle vous ne pouvez pas comprendre comment accomplir ce que vous essayez de faire est probablement que ce que vous essayez de faire n'a pas vraiment de sens. Vous souhaitez appeler getInstance(x)avec des arguments différents, mais renvoyez toujours le même objet? Quel comportement voulez-vous lorsque vous appelez getInstance(2)et ensuite getInstance(5)?

Si vous voulez que le même objet soit différent mais que sa valeur interne soit différente, ce qui est la seule façon dont il reste un singleton, alors vous n'avez pas du tout à vous soucier du constructeur; vous venez de définir la valeur getInstance()d'entrée sur la sortie de l'objet. Bien sûr, vous comprenez que toutes vos autres références au singleton ont maintenant une valeur interne différente.

Si vous voulez getInstance(2)et getInstance(5)renvoyer différents objets, d'un autre côté, vous n'utilisez pas le modèle Singleton, vous utilisez le modèle Factory.


3

Dans votre exemple, vous n'utilisez pas de singleton. Notez que si vous effectuez les opérations suivantes (en supposant que Singleton.getInstance était en fait statique):

Singleton obj1 = Singleton.getInstance(3);
Singleton obj2 = Singleton.getInstance(4);

Alors les valeurs de obj2.x sont 3, pas 4. Si vous devez faire cela, faites-en une classe simple. Si le nombre de valeurs est petit et fixe, vous pouvez envisager d'utiliser un fichier enum. Si vous rencontrez des problèmes de génération d'objets excessive (ce qui n'est généralement pas le cas), vous pouvez envisager de mettre en cache les valeurs (et vérifier les sources ou obtenir de l'aide, car il est évident comment créer des caches sans risque de fuite de mémoire).

Vous voudrez peut-être également lire cet article car les singletons peuvent être très facilement surutilisés.


3

Une autre raison pour laquelle les singletons sont un anti-pattern est que s'ils sont écrits selon les recommandations, avec un constructeur privé, ils sont très difficiles à sous-classer et à configurer pour être utilisés dans certains tests unitaires. Serait nécessaire pour maintenir le code hérité, par exemple.


3

Si vous souhaitez créer une classe Singleton servant de contexte, un bon moyen est d'avoir un fichier de configuration et de lire les paramètres à partir du fichier dans instance ().

Si les paramètres alimentant la classe Singleton sont obtenus de manière dynamique pendant l'exécution de votre programme, utilisez simplement un HashMap statique stockant différentes instances dans votre classe Singleton pour vous assurer que pour chaque paramètre, une seule instance est créée.


1

Ce n'est pas tout à fait un singleton, mais peut-être quelque chose qui pourrait résoudre votre problème.

public class KamilManager {

  private static KamilManager sharedInstance;

  /**
   * This method cannot be called before calling KamilManager constructor or else
   * it will bomb out.
   * @return
   */
  public static KamilManager getInstanceAfterInitialized() {
    if(sharedInstance == null)
        throw new RuntimeException("You must instantiate KamilManager once, before calling this method");

    return sharedInstance;
}

  public KamilManager(Context context, KamilConfig KamilConfig) {
    //Set whatever you need to set here then call:
  s  haredInstance = this;
  }
}

1

Si nous considérons le problème comme "comment créer un singleton avec un état", alors il n'est pas nécessaire de passer l'état comme paramètre de constructeur. Je suis d'accord avec les articles qui initialisent les états ou utilisent la méthode set après avoir obtenu l'instance singleton.

Une autre question est: est-il bon d'avoir un singleton avec un état?


1

Ne pourrions-nous pas faire quelque chose comme ça:

public class Singleton {

    private int x;

    // Private constructor prevents instantiation from other classes
    private Singleton() {}

    /**
     * SingletonHolder is loaded on the first execution of Singleton.getInstance() 
     * or the first access to SingletonHolder.INSTANCE, not before.
     */
    private static class SingletonHolder { 
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance(int x) {
        Singleton instance = SingletonHolder.INSTANCE;
        instance.x = x;
        return instance;
    }
}

1

Malgré ce que certains peuvent affirmer, voici un singleton avec des paramètres en constructeur

public class Singleton {

    private static String aParameterStored;

    private static final Singleton instance = new Singleton("Param to set");

    private Singleton() {
        // do nothing
    }

    private Singleton(String param) {
        aParameterStored = param;
    }

    public static Singleton getInstance() {
        return instance;
    }

    /*
     * ... stuff you would like the singleton do
     */
}

Le modèle singleton dit:

  • s'assurer qu'une seule instance de la classe singleton existe
  • fournir un accès global à cette instance.

qui sont respectés avec cet exemple.

Pourquoi ne pas définir directement la propriété? Il est normal de montrer comment nous pouvons obtenir un singleton ayant un constructeur avec paramètre, mais cela pourrait être utile dans certaines situations. Par exemple, dans les cas d'héritage pour forcer le singleton à définir certaines propriétés de superclasse.


0

J'ai peur de poster cela comme réponse, mais je ne comprends pas pourquoi personne n'y pense, peut-être que cette réponse a déjà été donnée, je ne l'ai tout simplement pas comprise.

public class example  {
    private volatile static example instance;

    private String string;
    private int iInt = -1; //any number you know you don't want to use here

  private example() {

    //In case someone uses the private method to create a new Instance
    if (instance != null){
      throw new RuntimeException("Use getInstance() method to get the single instance of this class.");
    }
  }

  public synchronized static example getIsntance(){
    if(instance == null){
      instance = new example();
    }
    return instance;
  }

public void methodDoingWork(){
    if(checkInit()){
      //DoSome
    }
  }

  private boolean checkInit(){
    boolean filled = (this.string != null) && (this.iInt != -1);
    return filled;
  }

  public void setString(String string) {
    if(this.string == null){
      this.string = string;
    }else{
      throw new RuntimeException("You try to override an already setValue"); 
    }
  }

  public void setiInt(int iInt) {
    if(this.iInt == -1){
      this.iInt = iInt;
    }else{
      throw new RuntimeException("You try to override an already setValue");
    }
  }
}

Puisque le getInstance()retourne la même instance à chaque fois, je pense que cela pourrait fonctionner. Si cela ne va pas trop, je le supprimerai, je suis juste intéressé par ce sujet.


-1

Je pense que c'est un problème courant. Séparer l '"initialisation" du singleton de la "get" du singleton pourrait fonctionner (cet exemple utilise une variante du verrouillage vérifié).

public class MySingleton {

    private static volatile MySingleton INSTANCE;

    @SuppressWarnings("UnusedAssignment")
    public static void initialize(
            final SomeDependency someDependency) {

        MySingleton result = INSTANCE;

        if (result != null) {
            throw new IllegalStateException("The singleton has already "
                    + "been initialized.");
        }

        synchronized (MySingleton.class) {
            result = INSTANCE;

            if (result == null) {
                INSTANCE = result = new MySingleton(someDependency);
            } 
        }
    }

    public static MySingleton get() {
        MySingleton  result = INSTANCE;

        if (result == null) {
            throw new IllegalStateException("The singleton has not been "
                    + "initialized. You must call initialize(...) before "
                    + "calling get()");
        }

       return result;
    }

    ...
}

Pourrait toujours renvoyer "result" dans la méthode initialize aussi je suppose.
Michael Andrews

-2

Singleton est, bien sûr, un "anti-pattern" (en supposant une définition d'un statique avec un état variable).

Si vous voulez un ensemble fixe d'objets à valeur immuable, les énumérations sont la solution. Pour un ensemble de valeurs volumineux, éventuellement ouvert, vous pouvez utiliser un référentiel d'une certaine forme, généralement basé sur une Mapimplémentation. Bien sûr, lorsque vous avez affaire à la statique, soyez prudent avec le threading (soit synchronisez suffisamment largement, soit utilisez une ConcurrentMapvérification qu'un autre thread ne vous a pas battu, soit utilisez une forme de futur).


4
Seulement un anti-motif s'il est utilisé de manière incorrecte, bien que ce soit la définition d'un anti-motif. Ce n'est pas parce que vous les avez vus là où ils n'appartenaient pas dans le passé qu'ils n'ont pas de place.
geowa4

L'utilisation correcte d'un singleton est de démontrer un code incompétent.
Tom Hawtin - tackline

-6

Les singletons sont généralement considérés comme des anti-motifs et ne doivent pas être utilisés. Ils ne facilitent pas le test du code.

Un singleton avec un argument n'a aucun sens de toute façon - que se passerait-il si vous écriviez:

Singleton s = SingletonHolder.getInstance(1);
Singleton t = SingletonHolder.getInstance(2); //should probably throw IllegalStateException

Votre singleton n'est pas non plus sûr pour les threads, car plusieurs threads peuvent effectuer des appels simultanés, getInstancece qui entraîne la création de plusieurs instances (éventuellement avec des valeurs différentes de x).


C'est assez discutable.
AlbertoPL

1
Oui, c'est discutable; d'où mon utilisation du mot «généralement». Je pense qu'il est juste de dire qu'ils sont généralement considérés comme une mauvaise idée
oxbow_lakes

C'est discutable - certaines personnes prétendent que ce qu'on appelle les «anti-modèles» correspondent à la définition des modèles, c'est juste qu'ils sont de mauvais modèles.
Tom Hawtin - tackline

Je comprends qu'ils sont mauvais. Je fais de l'informatique distribuée et j'ai besoin de partager un objet entre plusieurs tâches. Plutôt que d'initialiser de manière déterministe une variable statique, je voudrais faire abstraction de la logique dans un Singleton. J'imagine que je pourrais synchroniser getInstance. Cela fonctionnerait-il? Ce que je dois faire est de charger un fichier une fois pour de nombreuses tâches, et seulement après l'envoi de la première tâche. (Je ne veux pas que mes données soient sérialisées.) J'ai pensé que je ferais de mon AbstractFileReader un argument de la méthode getInstance afin de rendre le Singleton plus flexible. J'apprécie votre contribution.

Je pense que vous pouvez mal comprendre ce que signifie «distribué»? Il existe d'autres moyens de réaliser ce que vous voulez: avez-vous envisagé l'injection de dépendances? Ou JNDI?
oxbow_lakes
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.