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

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

其他回答

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

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

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

服务定位器隐藏了依赖关系——例如,当一个对象从定位器获取连接时,你不能通过观察对象来判断它是否访问了数据库。使用依赖注入(至少是构造函数注入),依赖关系是显式的。

此外,服务定位器打破了封装,因为它们提供了对其他对象的依赖关系的全局访问点。对于service locator,和任何单例一样:

很难指定前后 客户端对象的条件 接口,因为它的工作方式 可以对实现进行干预 从外面。

使用依赖注入,一旦指定了对象的依赖项,它们就处于对象本身的控制之下。

A class using constructor DI indicates to consuming code that there are dependencies to be satisfied. If the class uses the SL internally to retrieve such dependencies, the consuming code is not aware of the dependencies. This may on the surface seem better, but it is actually helpful to know of any explicit dependencies. It is better from an architectural view. And when doing testing, you have to know whether a class needs certain dependencies, and configure the SL to provide appropriate fake versions of those dependencies. With DI, just pass in the fakes. Not a huge difference, but it is there.

不过,DI和SL可以一起工作。为常见的依赖项(如设置、记录器等)设置一个中心位置是很有用的。给定一个使用这种deps的类,您可以创建一个接收deps的“真实”构造函数,以及一个从SL检索并转发给“真实”构造函数的默认(无参数)构造函数。

EDIT:当然,当您使用SL时,您将向该组件引入一些耦合。这是具有讽刺意味的,因为这种功能的思想是鼓励抽象和减少耦合。这些关注点是可以平衡的,这取决于您需要在多少地方使用SL。如果按照上面建议的那样做,只是在默认的类构造函数中。

The difference may seem slight, but even with the ServiceLocator, the class is still responsible for creating its dependencies. It just uses the service locator to do it. With DI, the class is given its dependencies. It neither knows, nor cares where they come from. One important result of this is that the DI example is much easier to unit test -- because you can pass it mock implementations of its dependent objects. You could combine the two -- and inject the service locator (or a factory), if you wanted.

在我的上一个项目中,我两者都用了。 我使用依赖注入进行单元测试。我使用服务定位器来隐藏实现并依赖于我的IoC容器。是的!一旦你使用了IoC容器(Unity, Ninject, Windsor Castle),你就依赖于它了。一旦它过时了,或者由于某种原因你想要交换它,你将/可能需要改变你的实现-至少是组合根。但是服务定位器抽象了这个阶段。

如何不依赖于IoC容器?您要么需要自己包装它(这是一个坏主意),要么使用Service Locator来配置IoC容器。因此,您将告诉Service Locator获取所需的接口,它将调用IoC容器,该容器被配置为检索该接口。

在我的例子中,我使用ServiceLocator,它是一个框架组件。我使用Unity作为IoC容器。如果在未来我需要交换我的IoC容器与Ninject,我所需要做的是配置我的服务定位器使用Ninject而不是Unity。容易迁移。

这里有一篇很棒的文章解释了这种情况; http://www.johandekoning.nl/index.php/2013/03/03/dont-wrap-your-ioc-container/