dependency injection和服务定位器模式有什么区别?

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

dependency injection(DI)似乎使用构造函数或setter来“注入”它的依赖关系。

使用构造函数注入的示例:

//Foo Needs an IBar public class Foo { private IBar bar; public Foo(IBar bar) { this.bar = bar; } //... } 

服务定位器似乎使用一个“容器”,它连接它的依赖关系,并给出它的条形码。

使用服务定位器的示例:

 //Foo Needs an IBar public class Foo { private IBar bar; public Foo() { this.bar = Container.Get<IBar>(); } //... } 

因为我们的依赖关系本身就是对象,所以这些依赖关系具有依赖关系,依赖关系更多,依此类推。 因此,控制容器的倒置(或称DI容器)诞生了。 例如:温莎城堡,Ninject,结构图,spring等)

但IOC / DI容器看起来完全像服务定位器。 称之为DI容器是一个坏名字? IOC / DI容器是另一种types的服务定位器吗? 我们使用DI容器时,大多数情况下我们有很多依赖关系?

差异可能看起来很小,但即使使用ServiceLocator,类仍然负责创build它的依赖关系。 它只是使用服务定位器来做到这一点。 用DI,这个类被赋予它的依赖关系。 它既不知道,也不在乎它们来自哪里。 这样做的一个重要结果就是DI例子更容易进行unit testing – 因为你可以通过模拟实现它的依赖对象。 你可以把两者结合起来 – 如果你想的话,注入服务定位器(或工厂)。

当您使用服务定位器时,每个类都将依赖于您的服务定位器。 dependency injection不是这种情况。 dependency injection器通常只会在启动时被调用一次,以将依赖关系注入某个主类。 这个主类依赖的类将recursion地注入它们的依赖关系,直到你有一个完整的对象图。

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

如果您的dependency injection器看起来像服务定位器,类直接调用注入器,它可能不是dependency injection器,而是服务定位器。

服务定位器隐藏依赖关系 – 当你从一个定位器获得连接时,你不能通过查看一个对象来判断它是否碰到数据库(例如)。 dependency injection(至less是构造函数注入)依赖是明确的。

此外,服务定位器破坏封装,因为它们提供了一个全局的访问其他对象的依赖关系的点。 有了服务定位器, 与任何单身人士一样 :

指定客户端对象接口的前后条件变得困难,因为其实现的工作可以从外部进行。

使用dependency injection时,一旦指定了对象的依赖关系,它们就受到对象本身的控制。

Martin Fowler指出

使用服务定位器,应用程序类通过消息明确要求定位器。 注入没有明确的请求,服务出现在应用程序类中 – 因此是控制的反转。

简而言之:服务定位器和dependency injection只是依赖倒置原理的实现。

重要原则是“取决于抽象,而不是结核”。 这将使您的软件devise“松耦合”,“可扩展”,“灵活”。

你可以使用最适合你的需求。 对于一个拥有庞大代码库的大型应用程序,最好使用服务定位器,因为dependency injection需要对代码库进行更多的更改。

你可以检查这个post: 依赖倒置:服务定位器或dependency injection

也是经典的: 控制容器的倒置和Martin Fowler的dependency injection模式

由Ralph E. Johnson&Brian Foote devise可重用类

然而,睁开眼睛的是: ASP.NET MVC:解决或注入? 这是迪诺·埃斯波西托的问题

使用构造函数DI的类表示需要使用依赖性满足的代码。 如果类在内部使用SL来检索这种依赖关系,那么消费代码就不知道依赖关系。 这可能在表面上看起来更好,但是知道任何明确的依赖关系实际上是有帮助的。 从build筑的angular度来看更好。 在进行testing时,您必须知道某个类是否需要某些依赖项,并configurationSL以提供这些依赖项的相应伪造版本。 用DI,只要通过假货。 没有太大的差别,但它在那里。

DI和SL可以一起工作。 有一个共同的依赖中心位置(例如设置,logging器等)是有用的。 给定一个使用这样的代码的类,你可以创build一个接收代码的“真实”构造函数,以及一个从SL中检索并转发给“真实”构造函数的默认(无参数)构造函数。

编辑:当然,当你使用SL,你正在引入一些耦合到该组件。 这是讽刺的,因为这种function的想法是鼓励抽象和减less耦合。 问题可以平衡,这取决于你需要多less地方使用SL。 如果按照上面的build议完成,只需在默认的类构造函数中完成。

我认为这两个工作在一起。

dependency injection意味着你将一些依赖类/接口推入一个消费类(通常是构造函数)。 这通过一个接口解耦这两个类,这意味着这个消费类可以处理很多types的“注入依赖”实现。

服务定位器的作用是把你的实现集中在一起。 您在程序开始时通过一些引导程序设置服务定位器。 引导是将实现types与特定的抽象/接口相关联的过程。 在运行时为您创build。 (基于你的configuration或引导)。 如果您还没有实现dependency injection,那么使用服务定位器或IOC容器将非常困难。

注意:我不完全回答这个问题。 但是我觉得这可能对dependency injection模式的新学习者来说很有用,他们被服务定位器(反模式)弄糊涂了,他们偶然发现了这个页面。

我知道服务定位器(它似乎被认为是反模式现在)和dependency injection模式之间的区别,可以理解每个模式的具体示例,但我很困惑的例子显示服务定位器内的构造(假设我们'重新做构造注入)。

“服务定位器”通常既用作模式的名称,也用作引用该模式中使用的对象(假设也是)的名称,而不使用新运算符来获取对象。 现在,同样types的对象也可以在组合根上使用来执行dependency injection,这就是混淆的地方。

需要注意的是,您可能正在DI构造函数中使用服务定位器对象,但是您没有使用“服务定位器模式”。 如果你把它作为一个IoC容器对象来引用,你可能会猜到它们基本上做了同样的事情(如果我错了,那么纠正我)就不那么容易混淆了。

无论是被称为服务定位器(或定位器),还是作为IoC容器(或只是容器),都没有什么区别,因为它们可能指的是相同的抽象(如果我错了,请纠正我)。 只是将其称为服务定位器,表明正在将服务定位器反模式与dependency injection模式一起使用。

恕我直言,将其命名为“定位器”而不是“位置”或“定位”,也可能导致人们有​​时会认为文章中的服务定位器是指服务定位器容器,而不是服务定位器(反模式)特别是当有一个叫做dependency injection而不是dependency injection的相关模式时。

我们补充的一个原因是受上周我们为MEF项目编写的文档更新(我帮助构buildMEF)的启发。

一旦应用程序由数千个组件组成,可能难以确定是否可以正确实例化任何特定的组件。 通过“实例化正确”,我的意思是在这个基于Foo组件的例子中, IBar一个实例将会可用,并且提供它的组件将:

  • 有其所需的依赖关系,
  • 不参与任何无效的依赖周期,
  • 在MEF的情况下,只提供一个实例。

在第二个示例中,在构造函数转到IoC容器以检索它的依赖关系的情况下,您可以testingFoo实例的唯一方法将能够正确地与应用程序的实际运行时configuration实例化构build它

这在testing时会产生各种尴尬的副作用,因为在运行时工作的代码不一定在testing工具下工作。 嘲笑不会,因为真正的configuration是我们需要testing的事情,而不是一些testing时间设置。

这个问题的根源是@Jon已经提出的差异:通过构造函数注入依赖是声明式的,而第二个版本使用命令式服务定位器模式。

使用IoC容器时,可以静态分析应用程序的运行时configuration,而无需实际创build任何涉及组件的实例。 许多stream行的容器提供了一些变化, Microsoft.Composition是针对.NET 4.5 web和Metro风格应用程序的MEF版本,在Wiki文档中提供了CompositionAssert示例。 使用它,你可以编写如下代码:

  // Whatever you use at runtime to configure the container var container = CreateContainer(); CompositionAssert.CanExportSingle<Foo>(container); 

(见这个例子 )。

通过在testing时validation应用程序的组合根 ,您可能会发现一些错误,这些错误可能会在以后的testing中通过testing。

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

两者都是IoC的实现技术。 还有其他一些实现控制反转的模式:

  • 工厂模式
  • 服务定位器
  • dependency injection(构造函数注入,参数注入(如果不是必需的),设置注入接口注入)…

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

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

在我上一个项目中,我使用了两个。 我使用dependency injection来实现单元可testing性。 我使用服务定位器来隐藏实现并依赖于我的IoC容器。 是的! 一旦你使用了一个IoC容器(Unity,Ninject,Windsor Castle),你就依赖它。 一旦过期或者由于某种原因,如果你想交换,你将/可能需要改变你的实现 – 至less是组成的根。 但是服务定位器抽象了这个阶段。

你不会依赖你的IoC容器? 要么你需要自己打包(这是一个坏主意),或者你使用服务定位器configuration你的IoC容器。 所以你会告诉服务定位器来获得你需要的接口,并且会调用configuration的IoC容器来检索那个接口。

在我的情况下,我使用ServiceLocator这是一个框架组件。 并使用Unity for IoC容器。 如果将来我需要将我的IoC容器交换到Ninject,我需要做的是我需要configuration我的服务定位器来使用Ninject而不是Unity。 轻松迁移。

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

在这个过于简单的情况下,没有什么区别,它们可以互换使用。 但是,现实世界的问题并不那么简单。 假设Bar类本身有另一个名为D的依赖项。在这种情况下,你的服务定位器将无法解决这个依赖项,你将不得不在D类中实例化它; 因为这是您的类的实例化他们的依赖关系的责任。 如果D类本身具有其他依赖关系,那么情况甚至会变得更糟,而在现实世界中,它通常会变得更加复杂。 在这种情况下,DI是比ServiceLocator更好的解决scheme。

dependency injection和服务定位器有什么区别(如果有的话)? 这两种模式都很好地实现了依赖倒置原则。 Service Locator模式更容易在现有的代码库中使用,因为它使整体devise更加宽松,而不会强制更改公共接口。 基于同样的原因,基于服务定位器模式的代码与基于dependency injection的等效代码相比,其可读性差。

dependency injection模式使得它清楚了,因为签名依赖于一个类(或一个方法)将具有的特征。 出于这个原因,生成的代码更干净,更具可读性。