27

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

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

我尝试创建抽象工厂作为容器的包装器,我可以将其注入需要创建对象的应用程序部分。这可行但有一些缺点,因为城堡很难将自己的容器作为依赖项注入。所以我必须手动完成,这违背了 IoC 容器的全部目的。

我使用主 applicationcontroller 类来包装 IoC 容器并作为中央工厂/存储库工作。这是相当成功的,但是这个类变得太大了,就像一个中心上帝对象,几乎所有其他对象都有对它的引用。

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


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

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

@花:嗯。我喜欢你的拳头解决方案。它结合了我尝试过的两种解决方案的工作原理。我想我仍然在对象方面考虑太多,而在接口/职责方面考虑得不够。我尝试了专门建造的工厂,但我想让他们在幕后使用容器来创建对象,但我还没有发现如何以干净的方式将容器 DI 转换为对象。

4

7 回答 7

12

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

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

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

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

于 2008-09-30T09:22:12.357 回答
11

我建议您查看 Nick Blumhardt 的迷你系列。

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

于 2009-02-01T08:16:24.290 回答
4

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

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

但是......如果你无论如何都要拥有所有这些接口,你可能最好编写专门构建的工厂类并将所有对象连接在一起,包括工厂,在容器中,而不是完全包装容器。容器仍然可以配置工厂。

于 2008-09-20T23:11:02.367 回答
2

虽然我很欣赏“专用工厂”的明确性,甚至自己也使用它们,但在我自己的设计中,这感觉就像是代码味道,因为公共接口(小“i”)会随着新工厂和/或新 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 框架,无论如何。

于 2008-09-23T13:50:40.067 回答
1

作为@flipdoubt的后续行动

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

祝你好运。

于 2008-10-12T07:07:58.837 回答
1

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

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

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

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

于 2008-10-22T18:09:03.817 回答
0

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.

于 2011-04-23T00:56:58.197 回答