依赖注入vs服务位置
我目前正在权衡DI和SL之间的优缺点。 但是,我发现自己在下面的catch 22中,这意味着我应该对所有内容都使用SL,并且只将IoC容器注入到每个类中。
DI Catch 22:
一些依赖性,如Log4Net,根本不适合DI。 我将这些元依赖称为“调用代码”,并认为它们应该是不透明的。 我的理由是,如果一个简单的类'D'最初是在没有日志记录的情况下实现的,然后增长到需要日志记录,那么依赖类'A','B'和'C'现在必须以某种方式获得这种依赖关系,并从'A'到'D'(假设'A'组成'B','B'组成'C'等等)。 我们现在已经做了重大的代码更改,因为我们需要登录一个类。
因此我们需要一个不透明的机制来获得元依赖关系。 两个想到:辛格尔顿和SL。 前者具有已知的局限性,主要是关于严格的范围界定能力:单纯的单点将使用存储在应用范围(即静态变量)中的抽象工厂。 这允许一些灵活性,但并不完美。
更好的解决方案是将IoC容器注入这些类中,然后使用该类中的SL从容器中解析这些元依赖关系。
因此赶上22:因为这个类现在被注入一个IoC容器,那么为什么不使用它来解决所有其他的依赖呢?
我会非常感谢你的想法:)
因为这个类现在被注入一个IoC容器,那么为什么不用它来解决所有其他的依赖呢?
使用服务定位器模式完全失败了依赖注入的一个要点。 依赖注入的重点是明确依赖关系。 一旦通过在构造函数中不使用显式参数来隐藏这些依赖关系,就不再进行全面的依赖注入。
这些都是名为Foo
的类的所有构造函数(设置为Johnny Cash歌曲的主题):
错误:
public Foo() {
this.bar = new Bar();
}
错误:
public Foo() {
this.bar = ServiceLocator.Resolve<Bar>();
}
错误:
public Foo(ServiceLocator locator) {
this.bar = locator.Resolve<Bar>();
}
对:
public Foo(Bar bar) {
this.bar = bar;
}
只有后者才会使Bar
对Bar
的依赖性显式化。
至于日志记录,有一个正确的方法来做到这一点,而不会渗透到你的域代码(它不应该,但如果它确实,那么你使用依赖注入期)。 令人惊讶的是,IoC容器可以帮助解决这个问题。 从这里开始。
Service Locator是一种反模式,出于在http://blog.ploeh.dk/2010/02/03/ServiceLocatorIsAnAntiPattern.aspx中出色描述的原因。 在日志记录方面,您可以像其他任何方式一样将它视为依赖项,并通过构造函数或属性注入来注入抽象。
与log4net的唯一区别是它需要使用该服务的调用方的类型。 使用Ninject(或其他容器)如何找出请求服务的类型? 介绍如何解决这个问题(它使用Ninject,但适用于任何IoC容器)。
或者,您可以将日志记录视为交叉切入问题,这不适合与业务逻辑代码混合使用,在这种情况下,您可以使用由许多IoC容器提供的拦截。 http://msdn.microsoft.com/en-us/library/ff647107.aspx描述了使用Unity进行拦截。
我的看法是,这取决于。 有时一个更好,有时另一个更好。 但我会说通常我更喜欢DI。 这有几个原因。
当依赖注入某个组件时,它可以被视为其接口的一部分。 因此组件的用户更容易提供这些依赖关系,因为它们是可见的。 在注入SL或静态SL的情况下,隐藏依赖关系并且组件的使用有点困难。
注入的依赖对单元测试更好,因为你可以简单地嘲笑它们。 在SL的情况下,你必须再次设置定位器+模拟依赖关系。 所以这是更多的工作。