这两种模式似乎都是控制反转原理的实现。也就是说,一个对象不应该知道如何构造它的依赖项。
依赖注入(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/
服务定位器和依赖注入都是遵循依赖倒置原则的对象访问模式实现
依赖注入是[static/global]对象访问模式
服务定位器是[动态]对象访问模式
如果你需要处理[动态结构],如[ui树]或任何[分形设计的应用程序],你可能需要服务定位器。
例子:
React的createContext/useContext
提供/注入Vue
angular的提供者
如果您只想从类中获取一个实例,而不关心应用程序的层次结构以及实例在该层次结构中的位置,那么您应该使用DI。
例子:
c# /Java中的注释
如果在运行时之前不知道服务的实际提供者,则使用Service Locator。
当您知道提供该服务的是静态容器时,就使用DI。
服务定位器模式更像是模块级别的依赖提供程序,而依赖注入模式是全局级别的。
当有一个子模块声明了一个服务的依赖关系,该服务应该由它的父模块提供,而不是静态解析类型(单例/瞬态/静态作用域)时,这是非常有用的。
它可以通过DI的作用域注入模式实现,而作用域则由应用程序的模块结构/关系定义。
个人建议:
尽可能使用DI。
如果你必须处理分形结构中的动态/运行时服务解析,请使用Service Locator。
将服务定位符封装为一个有作用域的DI,例如:
通过接口定位服务,而不是通过类/构造函数。
详细信息:https://learn.microsoft.com/zh-cn/dotnet/core/extensions/dependency-injection-guidelines#recommendations
在我的上一个项目中,我两者都用了。
我使用依赖注入进行单元测试。我使用服务定位器来隐藏实现并依赖于我的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/