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!
Réponses:
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:
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:
Dans tous les cas, l'instanciation sera toujours sans paramètre. Sinon, votre singleton n'est pas un singleton.
la source
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é
HashMap
ou enConcurrentHashMap
mappant un paramètre (Integer
un 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:
Pour pousser encore plus loin, les Java
enum
peuvent é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.
la source
Vous pouvez ajouter une méthode d'initialisation configurable afin de séparer l'instanciation de l'obtention.
Ensuite, vous pouvez appeler
Singleton.init(123)
une fois pour le configurer, par exemple au démarrage de votre application.la source
Vous pouvez également utiliser le modèle Builder si vous souhaitez montrer que certains paramètres sont obligatoires.
Ensuite, vous pouvez le créer / instancier / paramétrer comme suit:
la source
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.
la source
Utilisez des getters et des setters pour définir la variable et rendre le constructeur par défaut privé. Puis utilisez:
la source
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é.
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.
la source
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):
la source
finally { RInterfaceHL.rloopHandler = null; }
engetInstance
, 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 parRInterfaceHL
ctor pour obtenir des valeurs, et non pour en garder une référence.return SingletonHolder.INSTANCE
fonctionnerait aussi bien dansgetInstance
. 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 arloopHandler
besoin 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.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 appelezgetInstance(2)
et ensuitegetInstance(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)
etgetInstance(5)
renvoyer différents objets, d'un autre côté, vous n'utilisez pas le modèle Singleton, vous utilisez le modèle Factory.la source
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):
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.
la source
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.
la source
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.
la source
Ce n'est pas tout à fait un singleton, mais peut-être quelque chose qui pourrait résoudre votre problème.
la source
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?
la source
Ne pourrions-nous pas faire quelque chose comme ça:
la source
Malgré ce que certains peuvent affirmer, voici un singleton avec des paramètres en constructeur
Le modèle singleton dit:
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.
la source
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.
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.la source
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é).
la source
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
Map
implémentation. Bien sûr, lorsque vous avez affaire à la statique, soyez prudent avec le threading (soit synchronisez suffisamment largement, soit utilisez uneConcurrentMap
vérification qu'un autre thread ne vous a pas battu, soit utilisez une forme de futur).la source
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:
Votre singleton n'est pas non plus sûr pour les threads, car plusieurs threads peuvent effectuer des appels simultanés,
getInstance
ce qui entraîne la création de plusieurs instances (éventuellement avec des valeurs différentes dex
).la source