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

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


当前回答

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

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

然而,我们只能在具体的消费者使用的背景下讨论它们之间的区别。当在组合根目录中使用Service Locator和DI 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/

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

注意:我并不是在回答这个问题。但是我觉得这对于那些对依赖注入模式和服务定位器(反)模式感到困惑的新学习者来说是有用的。

我知道服务定位器(它现在似乎被视为反模式)和依赖注入模式之间的区别,并且可以理解每种模式的具体示例,但我对在构造函数内部显示服务定位器的示例感到困惑(假设我们正在进行构造函数注入)。

“Service Locator”通常既用作模式的名称,也用作该模式中使用的对象的名称(假设也是),以在不使用new操作符的情况下获取对象。现在,同样类型的对象也可以在组合根上用于执行依赖项注入,这就是产生混淆的地方。

需要注意的一点是,您可以在DI构造函数中使用服务定位器对象,但您没有使用“服务定位器模式”。如果将它引用为IoC容器对象,就不会那么令人困惑,因为您可能已经猜到它们本质上做的是相同的事情(如果我错了,请纠正我)。

无论它被称为服务定位器(或仅仅是定位器),还是IoC容器(或仅仅是容器),正如您所猜测的那样,它们都可能引用相同的抽象(如果我说错了,请纠正我)。只是称其为服务定位器表明将服务定位器反模式与依赖注入模式一起使用。

以我之见,将其命名为“定位器”而不是“位置”或“定位”,有时也会导致人们认为文章中的服务定位器指的是服务定位器容器,而不是服务定位器(反)模式,特别是当有一个相关的模式叫做依赖注入而不是依赖注入时。

郑重声明

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

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

  //...
}

除非你真的需要一个接口(接口被多个类使用),否则你绝对不能使用它。在这种情况下,IBar允许使用实现它的任何服务类。然而,通常,这个接口将由单个类使用。

为什么使用接口不是一个好主意?因为它真的很难调试。

例如,假设实例“bar”失败了,问题:哪个类失败了?我应该修复哪些代码?一个简单的视图,它指向一个界面,我的路到此结束。

相反,如果代码使用硬依赖项,则很容易调试错误。

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

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

  //...
}

如果“酒吧”失败,那么我应该检查和杉木类BarService。

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

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

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