IoC,我们将容器放在哪里?

时间:2020-03-06 14:29:43  来源:igfitidea点击:

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

我已经使用两种解决方案来解决此问题

我试图创建抽象工厂作为容器周围的包装,然后将其注入到应用程序中需要创建对象的部分中。这是可行的,但有一些缺点,因为Castle很难将其自己的容器作为依赖项进行注入。因此,我必须手动执行此操作,这会破坏IoC容器的整个目的。

我已经使用了主要的applicationcontroller类来包装IoC容器并用作中央工厂/存储库。这是非常成功的,但是这个类太大了,像一个中心神对象那样工作,几乎所有其他对象都对此有一个引用。

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

编辑
问题不在于依赖于对象B的对象A。在这里,我通常只使用构造函数注入,并且一切正常。有时我有A型对象,这些对象在其生命周期内需要创建数量可变的其他B型对象。我不确定该怎么做。

@Blair Conrad:到目前为止,维护问题并不严重。我有一些类依赖于调用container.Resolve <>的容器对象。而且我不想根据我认为的基础结构来编写代码。我仍在尝试,所以我注意到在从ninject切换到Castle进行此项目时,我不得不更改很多代码。

@花:嗯。我喜欢你的拳头解决方案。它结合了我尝试过的两种解决方案都能发挥的作用。我想我仍然在对象方面思考过多,而在界面/职责方面考虑不够。
我尝试了专门建造的工厂,但我想让它们在后台使用容器创建对象,但是我还没有找到如何将容器以干净的方式分解为对象的方法。

解决方案

至少在我的应用程序中,依赖注入的主要好处是能够编写与上下文无关的代码。从这个角度来看,第二个解决方案似乎真的破坏了DI可能给我们带来的好处。如果"上帝对象"向引用它的每个类公开了不同的接口,那么它可能不会太邪恶。但是,如果我们走了那么远,我不明白为什么我们不会一路走到铁环。

示例:God对象具有getFoo()方法和getBar()方法。对象A需要Foo,对象B需要Bar。如果A只需要一个Foo,则应该将Foo直接注入A,而A根本不应该认识神。但是,如果A需要继续创造敌人,那么给A提及上帝几乎是不可避免的。但是,通过缩小对上帝的提及的类型,我们可以保护自己免受上帝的伤害。如果我们让God实现FooFactory并给A引用由God实现的FooFactory,我们仍然可以以与上下文无关的方式用A编写代码。这增加了代码重用的机会,并增加了信心,即对上帝的改变不会引起意想不到的副作用。例如,当我们从上帝删除getBar()时,我们可以确定A类不会中断。

但是...如果仍然要拥有所有这些接口,则最好编写专用的工厂类并将所有对象(包括工厂在内)连接在一起,而不是将容器包装起来。容器仍可以配置工厂。

虽然我欣赏"专用工厂"的明确性,甚至自己使用它们,但在我自己的设计中却感觉像是代码的气味,因为公共接口(小" i")随着新工厂和/或者新GetX方法的变化而不断变化对于每个实现。在阅读了杰里米·米勒(Jeremy Miller)的《 IoC Container Detente的时机》之后,我怀疑应该使用仿制药,而注入容器本身是必经之路。

我会将Ninject,StructureMap或者Windsor包装在某种IServiceLocator接口中,例如Jeremy文章中提出的接口。然后拥有一个容器工厂,该容器工厂仅在代码中的任何地方返回IServiceLocator,甚至可以按照我们最初建议的循环返回一个IServiceLocator。

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

容器工厂始终可以返回相同的实例,可以交换IoC框架,无论如何。

请不要使用诸如IoC.Container.Resolve或者ContainerFactory.GetContainer之类的静态类!

这使代码更加复杂,难以测试以维护,重用和阅读。

通常,任何单个组件或者服务都只有一个注入点,即构造函数(具有可选属性)。通常,组件或者服务类永远都不应该知道诸如容器之类的东西的存在。

如果组件确实需要在内部进行动态解析(即根据名称解析异常处理策略或者工作流),那么我建议我们考虑通过高度特定的提供商借出IoC权力

作为@flipdoubt的后续活动

如果我们确实使用了服务定位器类型模式,则可能需要查看http://www.codeplex.com/CommonServiceLocator。它具有一些流行的IoC框架(windsor,structuremap)可用的绑定,这些绑定可能会有所帮助。

祝你好运。

在这种情况下,我建议使用我们提到的注入强类型的工厂。这些工厂可以包装容器,但可以允许传入其他上下文并进行额外的处理。例如,在OrderFactory上创建可以接受上下文参数。

对通用服务定位器具有静态依赖关系是一个坏主意,因为我们会松散意图和上下文。当IoC建立实例时,它可以根据很多因素提供适当的依赖关系,例如proifle,context等,因为它具有广阔的前景。

CommonServiceLocator并非出于此目的,尽管可能会尝试使用它。 CommonServiceLocator的主要目的是用于希望跨IoC容器兼容的应用程序/框架。但是,使用的应用程序应仅以最佳方式调用一次定位器,以建立组件及其依赖关系的层次结构。永远不要再次直接调用它。如果我们有某种方法可以执行,那我们就会有。在Prism(http://www.microsoft.com/compositewpf)中,我们介绍了用于构建模块的IContainerFacade。这是一个较低级别的服务定位器。回想起来,我们可能应该创建一个ModuleFactory或者其他东西,并使用IContianerFacade来获取它,然后使用解析模块而不是直接进入Facade。 Hindsight是20 /20。尽管它不会真正影响事物,但它的水平足够低。

在CSL上,我们努力命名,因为这可能会导致混淆。最后,我们决定使用CSL,因为从技术上讲,该接口不适合我们进行DI。

我建议我们查看Nick Blumhardt的迷你剧集。

http://blogs.msdn.com/nblumhardt/archive/2008/12/27/container-managed-application-design-prelude-where-does-the-container-belong.aspx