Question

J'utilise Castle Windsor pour un projet animalier sur lequel je travaille. Je commence à remarquer que je dois appeler le conteneur IoC à différents endroits de mon code pour créer de nouveaux objets. Cette dépendance au conteneur rend mon code plus difficile à gérer.

J'ai utilisé deux solutions pour résoudre ce problème

J'ai essayé de créer des fabriques abstraites en guise de wrappers autour du conteneur, que je pouvais injecter dans des parties de mon application nécessitant la création d'objets. Cela fonctionne mais présente certains inconvénients car Castle a du mal à injecter son propre conteneur en tant que dépendance. Donc, je dois le faire à la main, ce genre de défaites tout le but du conteneur IoC.

J'ai utilisé la classe principale applicationcontroller pour encapsuler le conteneur IoC et travailler comme une usine / un référentiel central. Cela a été assez réussi, mais cette classe devient trop grosse et agit comme un objet divin central, presque tous les autres objets y sont référencés.

Les deux solutions fonctionnent plutôt bien, mais les deux ont leurs inconvénients. Je suis donc curieux de savoir si d’autres personnes ont eu le même problème et ont trouvé de meilleures solutions.

modifier Le problème ne concerne pas l'objet A qui dépend de l'objet B. Ici, j'utilise généralement l'injection du constructeur et tout fonctionne. Parfois, j'ai des objets de type A qui ont besoin de créer un nombre variable d'autres objets de type B au cours de leur vie. Je ne sais pas comment faire cela.

@Blair Conrad: Les problèmes de maintenance ne sont pas graves jusqu'à présent. Certaines classes dépendaient de l'objet conteneur appelant container.Resolve & Lt; & Gt ;. Et je ne veux pas que mon code dépende de ce que je pense être une infrastructure. J'essaie toujours, alors j'ai remarqué que je devais changer beaucoup de code lorsque je passais de ninject à castle pour ce projet.

@flowers: Hmm. J'aime votre solution de poings. Il combine les choses qui fonctionnent des deux solutions que j'ai essayées. Je pense que je pensais encore trop aux objets et pas assez aux interfaces / responsabilités. J'ai essayé des usines construites à cet effet, mais j'aimerais qu'ils utilisent le conteneur dans les coulisses pour créer les objets et je n'ai pas trouvé comment je pourrais le transformer en objets d'une manière propre.

Était-ce utile?

La solution

Le principal avantage de Dependency Injection, du moins dans mes applications, est la possibilité d'écrire du code indépendant du contexte. De ce point de vue, votre deuxième solution semble avoir pour effet de réellement fausser l’avantage que l’ID pourrait vous apporter. Si «l'objet divin» expose différentes interfaces à chaque classe qui le référence, il ne sera peut-être pas trop maléfique. Mais si vous êtes allé aussi loin, je ne vois pas pourquoi vous ne le menez pas jusqu'au cerceau.

Exemple: votre objet Dieu possède une méthode getFoo () et une méthode getBar (). L'objet A a besoin d'un Foo, l'objet B a besoin d'un Bar. Si A a juste besoin d'un Foo, Foo devrait être injecté directement dans A et A ne devrait pas du tout être conscient de Dieu. Mais si A doit continuer à créer Foos, donner à A une référence à Dieu est quasiment inévitable. Mais vous pouvez vous protéger des dommages causés en contournant Dieu en restreignant le type de référence à Dieu. Si vous faites que Dieu implémente FooFactory et donnez à A une référence au FooFactory mis en œuvre par Dieu, vous pouvez toujours écrire le code dans A de manière neutre par rapport au contexte. Cela améliore les possibilités de réutilisation du code et augmente votre confiance qu'un changement de Dieu ne causera pas d'effets secondaires inattendus. Par exemple, lorsque vous supprimez getBar () de Dieu, vous pouvez être certain que la classe A ne sera pas interrompue.

MAIS ... si vous avez de toute façon toutes ces interfaces, vous feriez probablement mieux d'écrire des classes fabriques spécifiques et de câbler tous vos objets ensemble, usines incluses, dans le conteneur plutôt que d'envelopper le conteneur. du tout. Le conteneur peut toujours configurer les usines.

Autres conseils

N'utilisez jamais de classes statiques telles que IoC.Container.Resolve ou ContainerFactory.GetContainer!

Cela rend le code plus compliqué, plus difficile à tester pour maintenir, pour réutiliser et pour lire.

Normalement, tout composant ou service n'a qu'un seul point d'injection, à savoir le constructeur (avec des propriétés facultatives). Et en général, vos composants ou classes de service ne doivent jamais savoir qu’il existe un conteneur.

Si vos composants ont réellement besoin d'une résolution dynamique (résolution de la politique de gestion des exceptions ou du flux de travaux, en fonction du nom), il est recommandé de prendre en compte octroyer des pouvoirs d'IoC via des fournisseurs hautement spécifiques

Bien que j'apprécie le caractère explicite des & "Usines construites à cet effet &"; et même les utiliser moi-même, cela ressemble à une odeur de code dans mes propres conceptions, car l'interface publique (petit & «i &»;) change constamment avec une nouvelle usine et / ou une nouvelle méthode GetX pour chaque implémentation. . Après avoir lu le document Il est temps pour IoC Container Detente , je suppose que les génériques et que l'injection du conteneur lui-même est la solution.

Je placerais Ninject, StructureMap ou Windsor dans une sorte d’interface IServiceLocator telle que celle proposée dans l’article de Jeremy. Créez ensuite une fabrique de conteneurs qui renvoie simplement un IServiceLocator n’importe où dans votre code, même dans les boucles suggérées à l’origine.

IServiceLocator container = ContainerFactory.GetContainer(); 
while( keepLooping )
{
    IExample example = container.GetInstance<IExample>();
    keepLooping = example.DoWork();
}

Votre usine de conteneurs peut toujours retourner la même chose, vous pouvez échanger les frameworks IoC, peu importe.

Suite de @flipdoubt

Si vous finissez par utiliser un modèle de type de localisateur de service, vous pouvez vérifier http: // www. codeplex.com/CommonServiceLocator . Des liaisons disponibles avec plusieurs frameworks IoC populaires (windsor, structuremap) pourraient être utiles.

Bonne chance.

Je recommanderais dans ce cas d’utiliser des usines fortement typées, comme vous l’avez mentionné, qui sont injectées. Ces usines peuvent emballer le conteneur, mais peuvent permettre de passer dans un contexte supplémentaire et d'effectuer une manipulation supplémentaire. Par exemple, la création sur OrderFactory peut accepter des paramètres contextuels.

Avoir des dépendances statiques sur un localisateur de service générique est une mauvaise idée car vous perdez l’intention et le contexte. Lorsqu'un IoC construit une instance, il peut fournir les dépendances correctes en fonction d'une foule de facteurs tels que proifle, contexte, etc., car il donne une vue d'ensemble.

CommonServiceLocator n’est pas conçu à cette fin, bien que l’on puisse être tenté de l’utiliser. CommonServiceLocator est principalement destiné aux applications / infrastructures qui souhaitent se conformer à plusieurs conteneurs IoC. Cependant, les applications qui utilisent ne doivent appeler le localisateur qu'une seule fois de manière optimale pour créer une hiérarchie des composants et de leurs dépendances. Il ne devrait jamais être appelé directement à nouveau. Si nous avions un moyen d'imposer cela, nous l'aurions. Dans Prism ( http://www.microsoft.com/compositewpf ), nous avons introduit un IContainerFacade pour la construction modules. Il s’agit d’un localisateur de services, bien que de niveau inférieur. Rétrospectivement, nous aurions probablement dû créer une ModuleFactory ou quelque chose d’autre et utiliser IContianerFacade pour la récupérer, puis utiliser cette méthode pour résoudre les modules et non pour aller directement à la façade. Le recul est de 20/20. Son niveau est suffisamment bas pour que cela n’affecte pas vraiment les choses.

Sur CSL, nous nous sommes débattus avec le nom car cela risquait de semer la confusion. En fin de compte, nous avons choisi CSL car, techniquement, l'interface ne vous permettait pas de faire de l'ID.

C'est un problème vraiment commun. La installation typée d'usine de Windsor vous fournira les avantages d'utiliser une usine, sans les inconvénients mentionnés.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top