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