可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I'm using castle windsor for a pet-project I'm working on. I'm starting to notice that I need to call the IoC container in different places in my code to create new objects. This dependency on the container makes my code harder to maintain.
There are two solutions I've used to solve this problem
I tried to create abstract factories as wrappers around the container that I could inject into parts of my application that need to create objects. This works but has some drawbacks because castle has a hard time injecting it's own container as a dependency. So I have to do that by hand, this kind of defeats the whole purpose of the IoC container.
I have used the main applicationcontroller class to wrap the IoC container and work as a central factory/repository. This was quite succesfull but this class is getting too big and acts like a central god-object, almost every other objects has a reference to it.
Both solutions sort of work but both have their drawbacks. So I'm curious if other people had the same problem and have found better solutions.
edit
The problem isn't for object A that depends on object B. Here I usually just use constructor injection and everything works. Sometimes I have objects of type A that need to create a variable number of other objects of type B during their lifetime. I'm not sure how to do this.
@Blair Conrad: The maintenance issues are not severe until now. I had some classes depend on the container object calling container.Resolve<>. And I don't want to have my code depending on what I think is infrastructure. I'm still trying things out so I noticed I had to change a lot of code when switching from ninject to castle for this project.
@flowers: Hmm. I like your fists solution. It combines the things that work from both solutions I've tried. I think I was still thinking too much in objects and not enough in interfaces/responsibilities.
I tried purpose built factories but I would like to have them use the container behind the scenes to create the objects and I havn't found out how I can DI the container into objects in a clean way.
回答1:
The main benefit of Dependency Injection, at least in my applications, is the ability to write code that is context agnostic. From that perspective, your second solution seems like it really subverts the benefit DI could be giving you. If the 'god object' exposes different interfaces to each class that references it, it might not be too evil. But if you went that far I don't see why you don't take it all the way to the hoop.
Example: Your God object has a getFoo() method and a getBar() method. Object A needs a Foo, object B needs a Bar. If A just needs one Foo, Foo should be injected directly into A and A should not be aware of God at all. But if A needs to keep creating Foos, giving A a reference to God is pretty much inevitable. But you can protect yourself from the damage done by passing God around by narrowing the type of the reference to God. If you make God implement FooFactory and give A a reference to the FooFactory implemented by God, you can still write the code in A in a context-neutral way. That improves the opportunities for code reuse, and it increases your confidence that a change to God will not cause unexpected side-effects. For example, you can be certain when removing getBar() from God that class A won't break.
BUT ... if you're going to have all those interfaces anyway, you're probably better off writing purpose-built factory classes and wiring all your objects together, factories included, within the container, rather than wrapping the container at all. The container can still configure the factories.
回答2:
Please, do not ever ever use static classes like IoC.Container.Resolve or ContainerFactory.GetContainer!
This makes the code more complicated, harder to test to maintain, to reuse and to read.
Normally any single component or a service has only one single point of injection - that's the constructor (with optional properties). And generally your components or service classes should not ever know about the existence of such thing as container.
If your components really need to have dynamic resolution inside (i.e. resolving exception handling policy or workflow, based on the name), then I recommend to consider lending IoC powers via the highly-specific providers
回答3:
I'd recommend checking out Nick Blumhardt's mini-series on this.
http://blogs.msdn.com/nblumhardt/archive/2008/12/27/container-managed-application-design-prelude-where-does-the-container-belong.aspx
回答4:
While I appreciate the explicitness of "purpose built factories" and even use them myself, this feels like a code smell in my own designs because the public interface (little "i") keeps changing with a new factory and/or a new GetX method for each implementation. After reading Jeremy Miller's It's time for IoC Container Detente, I suspect generics and injecting the container itself is the way to go.
I would wrap Ninject, StructureMap, or Windsor in some kind of IServiceLocator interface like the one proposed in Jeremy's article. Then have a container factory that simply returns an IServiceLocator anywhere in your code, even in loops as you originally suggested.
IServiceLocator container = ContainerFactory.GetContainer();
while( keepLooping )
{
IExample example = container.GetInstance<IExample>();
keepLooping = example.DoWork();
}
Your container factory can always return the same intance, you can swap IoC frameworks, whatever.
回答5:
As a follow up to @flipdoubt
If you do end up using a service locator type pattern you may want to check out http://www.codeplex.com/CommonServiceLocator. It has some bindings available to several popular IoC frameworks (windsor, structuremap) that might be helpful.
Good luck.
回答6:
I would recommend in this case using strongly typed factories as you mentioned which get injected. Those factories can wrap the container, but can allow passing in additional context and do extra handling. For example the Create on the OrderFactory could accept contextual parameters.
Having static dependencies on a generic service locator is a bad idea as you loose the intent, and context. When an IoC builds up an instance, it can provide the correct dependencies based on a host of factors such as proifle, context, etc as it has the big picture.
CommonServiceLocator is not for this purpose, though one might be tempted to use it. The main purpose for CommonServiceLocator is for apps / frameworks that want to be cross IoC container compliant. However, apps that use should only call the locator optimally once to build up a hierarchy of components and their dependenices. It should never be directly called again. If we had some way to enforce that we would have. In Prism (http://www.microsoft.com/compositewpf) we introduced an IContainerFacade for building up modules. That is a service locator though a low level one. In retrospect we probably should have created a ModuleFactory or something and used IContianerFacade to get a hold of it, and then used that resolve modules vs going to the Facade directly. Hindsight is 20 / 20. It's low level enough though that it doesn't really affect things.
On CSL, We wrestled with the naming because it might lead to confusion. In the end we decided on CSL because technically the interface did not for you to do DI.
回答7:
That's a really comon problem. Windsor's built in Typed Factory Facility will give you the benefits of using a factory, without the mentioned drawbacks.