Nous avons une classe qui contient des informations de configuration pour l'application. C'était un singleton. Après un examen architectural, on nous a dit de retirer le singleton. Nous avons constaté certains avantages de ne pas utiliser singleton dans les tests unitaires, car nous pouvons tester différentes configurations à la fois.
Sans singleton, nous devons faire circuler l'instance partout dans notre code. Cela devient tellement désordonné que nous avons écrit un wrapper singleton. Maintenant, nous portons le même code vers PHP et .NET, je me demande s'il existe un meilleur modèle que nous pouvons utiliser pour l'objet de configuration.
Le blog de test Google contient une série d'entrées sur la façon d'éviter Singleton (afin de créer du code testable). Peut-être que cela peut vous aider:
Le dernier article explique en détail comment déplacer la création de nouveaux objets dans une usine, afin que vous puissiez éviter d'utiliser des singletons. À lire absolument.
En bref, nous transférons tous les nouveaux opérateurs dans une usine. Nous regroupons tous les objets de durée de vie similaire dans une seule usine.
La meilleure façon est d'utiliser un modèle Factory à la place. Lorsque vous construisez une nouvelle instance de votre classe (en 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 d'usine), soit en copiant les données pertinentes données dans le nouvel objet.
Tous vos objets contiendront alors les données qui vivaient dans le singleton. Je ne pense pas qu'il y ait beaucoup de différence dans l'ensemble, mais cela peut rendre votre code plus facile à lire.
Je peux énoncer l'évidence ici, mais y a-t-il une raison pour laquelle vous ne pouvez pas utiliser un cadre d'injection de dépendance tel que Spring ou Guice ? (Je pense que Spring est également disponible pour .NET maintenant).
De cette façon, le framework peut contenir une seule copie des objets de configuration, et vos beans (services, DAO, peu importe) n'ont pas à se soucier de la rechercher.
C'est l'approche que je prends habituellement!
n'accumulez pas de responsabilités sur un seul objet de configuration car il se terminera par un très gros objet à la fois difficile à comprendre et fragile.
Par exemple, si vous avez besoin d'un autre paramètre pour une classe particulière, vous modifiez l'objet Configuration
, puis recompilez toutes les classes qui l'utilisent. C'est quelque peu problématique.
Essayez de refactoriser votre code pour éviter un objet Configuration
commun, global et volumineux. Passez uniquement les paramètres requis aux classes client:
class Server {
int port;
Server(Configuration config) {
this.port = config.getServerPort();
}
}
devrait être refactorisé pour:
class Server {
public Server(int port) {
this.port = port;
}
}
un framework d'injection de dépendances aidera beaucoup ici, mais ce n'est pas strictement requis.
Si vous utilisez Spring Framework , vous pouvez simplement créer un bean standard. Par défaut (ou si vous définissez explicitement scope="singleton"
), Une seule instance du bean est créée et cette instance est renvoyée à chaque fois que le bean est utilisé dans une dépendance ou récupéré via getBean()
.
Vous obtenez l'avantage de l'instance unique, sans couplage du modèle Singleton.
L'alternative consiste à transmettre ce dont vous avez besoin au lieu de demander des choses à un objet.
Vous pouvez accomplir le même comportement de singleton en utilisant des méthodes statiques. Steve yegge l'explique très bien dans this post.
Examinez la possibilité de faire la configuration comme interface de rappel. Ainsi, votre code sensible à la configuration ressemblera:
MyReuseCode.Configure(IConfiguration)
Le code d'initialisation du système ressemblera à:
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 du code plutôt que du xml.
Une classe contenant uniquement des méthodes et des champs statiques est-elle possible? Je ne sais pas exactement quelle est votre situation, mais cela pourrait valoir la peine d'être examiné.
Peut-être pas très propre non plus, mais vous pourriez peut-être passer les bits d'information que vous souhaitez modifier à 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 setUp des tests unitaires).
Cela dépend de l'outillage/des cadres, etc. qui sont utilisés. Avec les outils d'injection de dépendance/ioc, il est souvent possible d'obtenir des performances/optimisations singleton en faisant en sorte que le conteneur di/ioc utilise le comportement singleton pour la classe requise - (comme une interface IConfigSettings) en ne créant jamais qu'une seule instance de la classe. Cela pourrait encore être remplacé par des tests
Alternativement, on pourrait utiliser une fabrique pour créer la classe et renvoyer la même instance chaque fois que vous la demandez - mais pour le tester, elle pourrait renvoyer une version tronquée/mockée