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

依赖注入(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容器。

其他回答

当您使用服务定位器时,每个类都依赖于您的服务定位器。依赖注入不是这样的。依赖注入器通常只在启动时被调用一次,以便将依赖注入到一些主类中。这个主类所依赖的类将递归地注入它们的依赖项,直到您有一个完整的对象图。

一个很好的对比:http://martinfowler.com/articles/injection.html

如果你的依赖注入器看起来像服务定位器,类直接调用注入器,那么它可能不是依赖注入器,而是服务定位器。

以下简单的概念让我更清楚地理解了Service Locator和DI Container的区别:

服务定位器用于消费者,它根据直接消费者的请求按ID从一些存储中提取服务 DI容器位于外部的某个地方,它从某个存储中获取服务并将它们推给消费者(无论是通过构造函数还是通过方法)

然而,我们只能在具体的消费者使用的背景下讨论它们之间的区别。当在组合根目录中使用Service Locator和DI Container时,它们几乎是相似的。

Martin Fowler说:

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

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

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

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

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

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

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

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

添加的一个原因是,受到我们上周为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根,您可以潜在地捕捉到一些错误,否则这些错误可能会在后面的测试过程中遗漏。

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

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