这两种模式似乎都是控制反转原理的实现。也就是说,一个对象不应该知道如何构造它的依赖项。

依赖注入(DI)似乎使用构造函数或setter来“注入”它的依赖项。

使用构造函数注入的例子:

//Foo Needs an IBar
public class Foo
{
  private IBar bar;

  public Foo(IBar bar)
  {
    this.bar = bar;
  }

  //...
}

Service Locator似乎使用了一个“容器”,它连接了它的依赖项并给了foo它的bar。

使用Service Locator的例子:

//Foo Needs an IBar
public class Foo
{
  private IBar bar;

  public Foo()
  {
    this.bar = Container.Get<IBar>();
  }

  //...
}

因为我们的依赖关系只是对象本身,这些依赖关系有依赖关系,依赖关系有更多依赖关系,等等。因此,控制反转容器(或DI容器)诞生了。例如:Castle Windsor, Ninject, Structure Map, Spring等)

但是IOC/DI容器看起来完全像服务定位器。称它为DI容器是一个坏名字吗?IOC/DI容器只是另一种类型的服务定位器吗?当我们有很多依赖时,我们使用依赖注入容器,这是一个细微的差别吗?


当前回答

它们都是IoC的实现技术。还有其他实现控制反转的模式:

工厂模式 服务定位器 DI (IoC)容器 依赖注入 (构造函数注入,参数注入(如果不需要),接口注入的setter注入) ...

服务定位器和DI容器看起来更相似,它们都使用容器来定义依赖关系,将抽象映射到具体实现。

主要的区别是依赖关系是如何定位的,在服务定位器中,客户端代码请求依赖关系,在DI容器中,我们使用容器来创建所有对象,并将依赖关系作为构造函数参数(或属性)注入。

其他回答

依赖注入和服务定位器之间有什么区别(如果有的话)?这两种模式都擅长实现Dependency Inversion原则。Service Locator模式更容易在现有代码库中使用,因为它使整体设计更松散,而无需强制更改公共接口。出于同样的原因,基于Service Locator模式的代码比基于依赖注入的等效代码可读性更差。

依赖注入模式从签名开始就明确了类(或方法)将具有哪些依赖项。因此,生成的代码更清晰,可读性更强。

In this oversimplified case there is no difference and they can be used interchangeably. However, real world problems are not as simple. Just assume that the Bar class itself had another dependency named D. In that case your service locator wouldn't be able to resolve that dependency and you would have to instantiate it within the D class; because it is the responsibility of your classes to instantiate their dependencies. It would even get worse if the D class itself had other dependencies and in real-world situations it usually gets even more complicated than that. In such scenarios DI is a better solution than ServiceLocator.

添加的一个原因是,受到我们上周为MEF项目编写的文档更新的启发(我帮助构建MEF)。

一旦应用程序可能由数千个组件组成,就很难确定任何特定的组件是否可以正确地实例化。通过“正确实例化”,我的意思是在这个基于Foo组件的例子中,IBar的实例和将是可用的,并且提供它的组件将:

有必要的依赖关系, 不涉及任何无效的依赖周期,以及 在MEF的情况下,只提供一个实例。

在你给出的第二个例子中,构造函数去IoC容器检索它的依赖项,你可以测试Foo实例能够正确地实例化应用程序的实际运行时配置的唯一方法是实际构造它。

这在测试时产生了各种尴尬的副作用,因为在运行时可以工作的代码不一定能在测试工具下工作。模拟是不行的,因为我们需要测试的是真正的配置,而不是一些测试时的设置。

这个问题的根源是@Jon已经指出的区别:通过构造函数注入依赖是声明性的,而第二个版本使用命令式的Service Locator模式。

IoC容器,如果使用得当,可以静态地分析应用程序的运行时配置,而无需实际创建任何相关组件的实例。许多流行的容器提供了这方面的一些变化;微软。Composition是针对。net 4.5 web和Metro风格应用程序的MEF版本,在wiki文档中提供了一个compostionassert示例。使用它,你可以编写如下代码:

 // Whatever you use at runtime to configure the container
var container = CreateContainer();

CompositionAssert.CanExportSingle<Foo>(container);

(请看这个例子)。

通过在测试时验证应用程序的Composition根,您可以潜在地捕捉到一些错误,否则这些错误可能会在后面的测试过程中遗漏。

希望这是一个有趣的补充,否则这个主题的答案是全面的!

Martin Fowler说:

使用服务定位器,应用程序类将显式地通过 消息发送给定位器。对于注入,没有显式的请求, 服务出现在应用程序类中——因此是 控制。

简而言之:服务定位器和依赖注入只是依赖反转原理的实现。

重要的原则是“依赖抽象,而不是具象”。这将使你的软件设计“松散耦合”、“可扩展”、“灵活”。

您可以使用最适合您需要的一种。对于拥有庞大代码库的大型应用程序,您最好使用服务定位器,因为依赖注入将需要对代码库进行更多更改。

你可以查看这篇文章:依赖倒置:服务定位器或依赖注入

还有经典的:Martin Fowler的控制容器反转和依赖注入模式

设计可重用类作者:Ralph E. Johnson和Brian Foote

然而,让我大开眼界的是:ASP。NET MVC:解析还是注入?这就是问题所在,迪诺·埃斯波西托著

DI容器是服务定位器的超集。它可用于定位服务,并具有组装(连接)依赖项注入的附加功能。