IoC,您将容器放在哪里?

问题描述 投票:24回答:7

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

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

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

我已经使用了主要的applicationcontroller类来包装IoC容器并充当中央工厂/存储库。这是非常成功的,但是这个类别变得太大了,并且起着中央神对象的作用,几乎所有其他对象都有对其的引用。

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


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

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

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

oop dependency-injection inversion-of-control castle-windsor
7个回答
3
投票

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

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

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


11
投票

请,永远不要使用IoC.Container.Resolve或ContainerFactory.GetContainer之类的静态类!

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

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

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



2
投票

虽然我欣赏“专用工厂”的明确性,甚至自己使用它们,但由于我的公共接口(小“ i”)随着新工厂和/或新工厂的不断变化,在我自己的设计中感觉像是代码的味道。每个实现的GetX方法。阅读Jeremy Miller的It's time for IoC Container Detente之后,我怀疑应该使用泛型,并且注入容器本身是可行的方法。

我将用某种IServiceLocator接口包装Ninject,StructureMap或Windsor,如Jeremy的文章中提出的那样。然后创建一个容器工厂,该工厂仅在代码的任何位置返回IServiceLocator,甚至可以按照您最初建议的循环返回。

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

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


1
投票

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

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

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

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


1
投票

作为对@flipdoubt的跟进

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

祝你好运。>>


0
投票

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

© www.soinside.com 2019 - 2024. All rights reserved.