IoC,你把容器放在哪里?

时间:2008-09-20 22:48:02

标签: oop dependency-injection inversion-of-control castle-windsor

我正在使用温莎城堡作为我正在进行的宠物项目。我开始注意到我需要在代码中的不同位置调用IoC容器来创建新对象。这种对容器的依赖使我的代码难以维护。

我用过两种解决方案来解决这个问题

我尝试创建抽象工厂作为容器周围的包装器,我可以将其注入到需要创建对象的应用程序的部分中。这有效,但有一些缺点,因为城堡很难将自己的容器注入依赖。所以我必须手动完成,这种方式会破坏IoC容器的整个目的。

我已经使用主应用程序控制器类来包装IoC容器并作为中央工厂/存储库工作。这是非常成功的,但这个类太大了,就像一个中心神对象,几乎所有其他对象都有它的参考。

这两种解决方案都有一些工作,但两者都有其缺点。所以我很好奇其他人是否有同样的问题,并找到了更好的解决方案。


修改的 问题不在于依赖于对象B的对象A.这里我通常只使用构造函数注入,一切正常。有时我有类型A的对象需要在其生命周期中创建可变数量的其他类型的B对象。我不知道该怎么做。

@Blair Conrad:到目前为止,维护问题并不严重。我有一些类依赖于容器对象调用container.Resolve<>。而且我不希望我的代码取决于我认为的基础设施。我还在尝试,所以我注意到在从这个项目的ninject切换到城堡时我必须更改很多代码。

@flowers:嗯。我喜欢你的拳头解决方案。它结合了我尝试过的两种解决方案。我认为我仍然在对象中思考太多而在界面/职责方面还不够。 我尝试过专门建造的工厂,但是我想让他们在幕后使用容器来创建对象,而我却没有发现如何以一种干净的方式将容器转移到对象中。

7 个答案:

答案 0 :(得分:11)

请不要使用像IoC.Container.Resolve或ContainerFactory.GetContainer这样的静态类!

这使代码更复杂,更难以维护,重用和读取。

通常,任何单个组件或服务只有一个单点注入 - 这是构造函数(具有可选属性)。通常,您的组件或服务类不应该知道容器这样的东西的存在。

如果你的组件确实需要内部动态解析(即根据名称解析异常处理策略或工作流),那么我建议考虑lending IoC powers via the highly-specific providers

答案 1 :(得分:10)

答案 2 :(得分:3)

至少在我的应用程序中,依赖注入的主要好处是能够编写与上下文无关的代码。从这个角度来看,你的第二个解决方案似乎真的颠覆了DI可能给你带来的好处。如果'god object'为引用它的每个类公开了不同的接口,那么它可能不会太邪恶。但如果你走得那么远,我不明白为什么你不把它一直带到篮筐。

示例:您的God对象有一个getFoo()方法和一个getBar()方法。对象A需要一个Foo,对象B需要一个Bar。如果A只需要一个Foo,Foo应该直接注入A和A根本不应该意识到上帝。但是,如果A需要继续创造Foos,那么给A提供上帝几乎是不可避免的。但是你可以通过缩小对上帝的引用类型来保护自己免受上帝绕过的伤害。如果你让上帝实现FooFactory并给A引用上帝实现的FooFactory,你仍然可以用上下文中立的方式在A中编写代码。这改善了代码重用的机会,并且增加了您对上帝的改变不会导致意外副作用的信心。例如,你可以确定从上帝那里移除getBar()时A类不会破坏。

但是......如果你打算拥有所有这些接口,你可能最好还是编写专用工厂类并将所有对象连接在一起,包括工厂,在容器内,而不是包装容器一点都不容器仍然可以配置工厂。

答案 3 :(得分:2)

虽然我很欣赏“专用工厂”的明确性,甚至自己使用它们,但这感觉就像我自己的设计中的代码味道,因为公共界面(小“我”)随着新工厂和/或每个实现的新GetX方法。在阅读了Jeremy Miller的It's time for IoC Container Detente之后,我怀疑仿制药并注入容器本身就是可行的方法。

我会将Ninject,StructureMap或Windsor包装在某种IServiceLocator接口中,就像Jeremy的文章中提到的那样。然后有一个容器工厂,它只是在你的代码中的任何地方返回一个IServiceLocator,甚至是你最初建议的循环。

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

您的容器工厂总是可以返回相同的内容,您可以交换IoC框架,无论如何。

答案 4 :(得分:1)

作为@flipdoubt

的后续行动

如果您最终使用服务定位器类型模式,则可能需要查看http://www.codeplex.com/CommonServiceLocator。它有一些绑定可用于几个流行的IoC框架(windsor,structuremap),可能会有所帮助。

祝你好运。

答案 5 :(得分:1)

我建议在这种情况下使用强力打字的工厂,如你所提到的那样注入。这些工厂可以包装容器,但可以允许在其他环境中传递并进行额外处理。例如,OrderFactory上的Create可以接受上下文参数。

在松散意图和上下文时,对通用服务定位器具有静态依赖性是一个坏主意。当IoC构建一个实例时,它可以根据许多因素提供正确的依赖关系,例如proifle,context等,因为它具有全局。

CommonServiceLocator不是为了这个目的,尽管可能会想要使用它。 CommonServiceLocator的主要用途是针对希望跨IoC容器兼容的应用程序/框架。但是,使用的应用程序应该只调用一次最佳定位器,以构建组件及其依赖项的层次结构。永远不应该再次直接调用它。如果我们有某种方式来执行我们会有的。在Prism(http://www.microsoft.com/compositewpf)中,我们引入了一个用于构建模块的IContainerFacade。这是一个低级别的服务定位器。回想起来,我们可能应该已经创建了一个ModuleFactory或者其他东西,并使用IContianerFacade来获取它,然后使用该解析模块直接转到Facade。后见之明是20 / 20.虽然它并没有真正影响事物,但它的水平还不够。

在CSL,我们与命名搏斗,因为它可能导致混乱。最后我们决定使用CSL,因为从技术上讲,界面并没有让你做DI。

答案 6 :(得分:0)

这是一个非常普遍的问题。温莎的内置Typed Factory Facility将为您提供使用工厂的好处,没有上述缺点。