IoC, Where do you put the container?

五迷三道 提交于 2019-11-28 04:10:07

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.

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

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.

smaclell

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.

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.

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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!