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

依赖注入(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容器只是另一种类型的服务定位器吗?当我们有很多依赖时,我们使用依赖注入容器,这是一个细微的差别吗?


当前回答

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.

其他回答

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。如果按照上面建议的那样做,只是在默认的类构造函数中。

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

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

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

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.

Martin Fowler说:

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

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

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

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

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

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

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

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

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

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