这两种模式似乎都是控制反转原理的实现。也就是说,一个对象不应该知道如何构造它的依赖项。
依赖注入(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容器只是另一种类型的服务定位器吗?当我们有很多依赖时,我们使用依赖注入容器,这是一个细微的差别吗?
Martin Fowler说:
使用服务定位器,应用程序类将显式地通过
消息发送给定位器。对于注入,没有显式的请求,
服务出现在应用程序类中——因此是
控制。
简而言之:服务定位器和依赖注入只是依赖反转原理的实现。
重要的原则是“依赖抽象,而不是具象”。这将使你的软件设计“松散耦合”、“可扩展”、“灵活”。
您可以使用最适合您需要的一种。对于拥有庞大代码库的大型应用程序,您最好使用服务定位器,因为依赖注入将需要对代码库进行更多更改。
你可以查看这篇文章:依赖倒置:服务定位器或依赖注入
还有经典的:Martin Fowler的控制容器反转和依赖注入模式
设计可重用类作者:Ralph E. Johnson和Brian Foote
然而,让我大开眼界的是:ASP。NET MVC:解析还是注入?这就是问题所在,迪诺·埃斯波西托著
服务定位器和依赖注入都是遵循依赖倒置原则的对象访问模式实现
依赖注入是[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