这有点违反 SO 的政策,但为了扩展 Simon 的回答,我将引导您阅读 Mark Seeman 的优秀博客文章:http ://blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Pattern/
一些对博客文章的评论也很有趣。
现在要解决您的 ninject 和扩展问题 - 我假设您/在编写它们时的组合根不知道 - 我想指出NinjectModule
s. Ninject 已经具有这种可扩展性机制,它被所有ninject.extension.XYZ
dll 大量使用。
你要做的是FooExtensionModule : NinjectModule
在你的扩展中实现一些类。模块包含Bind
方法。现在您将告诉 ninject 从某些 .dll 加载所有模块。而已。
这里有更详细的解释:https ://github.com/ninject/ninject/wiki/Modules-and-the-Kernel
缺点:
- 扩展依赖于 Ninject
- 当您“更新 ninject”时,您可能需要重新编译您的扩展
- 随着软件的发展,切换 DI 容器的成本会越来越高
- 使用时
Rebind
可能会出现难以追踪的问题(无论您是否使用模块,情况都是如此。)
- 尤其是当扩展开发人员不了解其他扩展时,他们可能会创建相同或冲突的绑定(例如
.Bind<string>().ToConst("Foo")
and .Bind<string>().ToConst("Bar")
)。同样,当您不使用模块时也是如此,但扩展增加了一层复杂性。
优点: - 简单明了,没有额外的复杂性/抽象层,您需要将容器抽象掉。
我NinjectModule
在一个不那么小的应用程序(15k 单元/组件测试)中使用了这种方法,并取得了很大的成功。
如果您只需要简单的绑定(例如.Bind<IFoo>().To<Foo>()
没有范围等),您还可以考虑使用更简单的系统,例如将属性放在类上,扫描这些属性,并在组合根中创建绑定。这种方法没有那么强大,但正因为如此,它也更“便携”(适用于其他 DI 容器)。
依赖注入延迟实例化
组合根的想法是,(尽可能)一次创建所有对象(整个对象图)。例如,在Main
您可能拥有的方法中kernel.Get<IMainViewModel>().Show()
。
然而,有时这是不可行或不合适的。在这种情况下,您将需要使用工厂。在 stackoverflow 上实际上已经有很多关于这方面的答案。有三种基本类型:
- 要创建一个
Foo
需要Dependency1
and实例的实例Dependency2
(注入 ctor),请创建一个类FooFactory
,该类获取一个实例Dependency1
和Dependency2
ctor 注入本身。然后该FooFactory.Create
方法将执行new Foo(this.dependency1, this.dependency2)
。
- 使用ninject.extensions.Factory:
- 用作
Func<Foo>
工厂:您可以进行Func<Foo>
注入,然后调用它来创建Foo
.
- 使用接口和
.ToFactory()
绑定(我推荐这种方法。更简洁的代码,更好的可测试性)。例如:IFooFactory
使用方法Foo Create()
。像这样绑定它:Bind<IFooFactory>().ToFactory();
替换实现的扩展
恕我直言,这不是依赖注入容器的目标之一。这并不意味着这是不可能的,而只是意味着你必须自己弄清楚。
ninject 最简单的方法是使用.Rebind<IFoo>().To<SomeExtensionsFoo>()
. 但是,如前所述,这有点脆弱。如果Bind
andRebind
以错误的顺序执行,则失败。如果有多个Rebind
s,最后一个会赢 - 但它是正确的吗?
所以让我们更进一步。想象:
`.Bind<IFoo>().To<SomeExtensionsFoo>().WhenExtensionEnabled<SomeExtension>();`
您可以设计自己的自定义WhenExtensionEnabled<TExtension>()
扩展方法来扩展When(Func<bool> condition)
语法方法。您必须设计一种方法来检测是否启用了扩展。