L'Alternative à Singleton
nous avons une classe qui contient des informations de configuration pour l'application. Il sert à être un singleton. Après une révision architecturale, on nous a dit d'enlever le singleton. Nous avons constaté certains avantages de ne pas utiliser singleton dans les essais unitaires, car nous pouvons tester différentes configurations en même temps.
sans singleton, nous devons passer l'instance partout dans notre code. C'est si bordélique qu'on a écrit un emballage de singleton. Maintenant nous portons le même code pour PHP et .NET, je me demande s'il y a un meilleur motif que nous pouvons utiliser pour l'objet de configuration.
13 réponses
Le Google blog Test a une série d'entrées d'éviter de Singleton (afin de créer de code de tests). Cela peut peut-être vous aider:
- à l'Aide de l'injection de dépendance pour éviter les singletons
- les Singletons sont des Menteurs Pathologiques
- cause profonde des Singletons
- où tous les Singleton sont partis?
le dernier article explique en détail comment déplacer la création de nouveaux objets dans une usine, de sorte que vous pouvez éviter d'utiliser des singletons. La peine de lire pour vous.
bref, nous déplaçons tous les nouveaux opérateurs dans une usine. Nous regroupons tous les objets de semblable à vie dans une seule usine.
la meilleure façon est d'utiliser un modèle D'usine à la place. Lorsque vous construisez une nouvelle instance de votre classe (dans l'usine), vous pouvez insérer les données 'globales' dans l'objet nouvellement construit, soit comme référence à une seule instance (que vous stockez dans la classe usine), soit en copiant les données pertinentes dans le nouvel objet.
tous vos objets contiennent alors les données qui habitaient dans le singleton. Je ne pense pas qu'Il ya beaucoup de différence dans l'ensemble, mais il peut rendre votre code plus facile à lire.
je pourrais dire l'évidence ici, mais y a-t-il une raison pour laquelle vous ne pouvez pas utiliser un cadre d'injection de dépendances tel que Spring ou Guice ? (Je crois que le printemps est aussi disponible pour .NET).
de cette façon, le framework peut contenir une seule copie des objets de configuration, et vos beans (services, DAOs, n'importe quoi) n'ont pas à s'inquiéter de le chercher.
C'est l'approche que j'ai d'habitude, prends!
Si vous utilisez Spring , vous pouvez simplement créer une fève. Par défaut (ou si vous définissez explicitement scope="singleton"
) une seule instance de la fève est créée et cette instance est retournée chaque fois que la fève est utilisée dans une dépendance ou récupérée via getBean()
.
vous obtenez l'avantage de l'instance simple, sans le couplage du modèle Singleton.
l'alternative est de passer dans ce dont vous avez besoin au lieu de demander un objet pour des choses.
n'accumule pas de responsabilités à un seul objet de configuration car il se termine par un très grand objet à la fois difficile à comprendre et fragile.
par exemple, si vous avez besoin d'un autre paramètre pour une classe particulière, vous changez l'objet Configuration
, puis vous recompilez toutes les classes qui l'utilisent. C'est quelque peu problématique.
essayez de refactoriser votre code pour éviter un commun, global et grand Configuration
objet. Transmettre uniquement les paramètres requis aux classes de clients:
class Server {
int port;
Server(Configuration config) {
this.port = config.getServerPort();
}
}
doit être remanié pour:
class Server {
public Server(int port) {
this.port = port;
}
}
un l'injection de dépendance cadre aidera beaucoup ici, mais il n'est pas strictement nécessaire.
est-ce qu'une classe qui ne contient que des méthodes et des champs statiques est possible? Je ne suis pas sûr de savoir exactement quelle est votre situation, mais ça pourrait valoir le coup d'examiner.
dépend de ce que l'outillage/les cadres etc.. sont utilisés. Avec les outils d'injection de dépendances / ioc, on peut souvent obtenir des performances/optimisations singleton en demandant au conteneur di/ioc d'utiliser un comportement singleton pour la classe requise - (comme une interface IConfigSettings) en ne créant qu'une seule instance de la classe. Cela pourrait encore être substitué à l'essai
alternativement on pourrait utiliser une usine pour créer la classe et retourner la même instance chaque fois que vous le demandez - mais pour les tests il pourrait retourner une version stubbed / mocked
examiner la possibilité de faire la configuration comme interface de rappel. Ainsi, votre code sensible à la configuration ressemblera à:
MyReuseCode.Configure(IConfiguration)
Système-code d'initialisation regardez:
Library.init(MyIConfigurationImpl)
vous pouvez utiliser un framework d'injection de dépendances pour soulager la douleur de passer dans l'objet de configuration. Un décent est ninject qui a l'avantage d'utiliser le code plutôt que xml.
peut-être pas très propre non plus, mais vous pourriez peut-être passer les bits d'information que vous voulez avoir changé à la méthode qui crée le singleton -- au lieu d'utiliser
public static Singleton getInstance() {
if(singleton != null)
createSingleton();
return singleton;
}
}
vous pouvez appeler createSingleton(Information info)
directement au démarrage de l'application (et dans les méthodes d'installation des tests unitaires).
les Singleton ne sont pas mauvais mais le dessin est défectueux. J'ai une classe que je ne veux créer qu'une seule instance pendant l'exécution mais que je veux créer plusieurs instances isolées pendant les tests unitaires pour assurer des résultats déterministes.
DI en utilisant le ressort, etc, est une très bonne option, mais pas la seule.