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

依赖注入(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容器。是的!一旦你使用了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/

其他回答

在我的上一个项目中,我两者都用了。 我使用依赖注入进行单元测试。我使用服务定位器来隐藏实现并依赖于我的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/

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

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

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

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

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

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

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

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.

Martin Fowler说:

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

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

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

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

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

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

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

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