ASP.NET Core依赖注入系统学习教程:3.Service Locator和依赖注入

在应用程序代码的每个类型中,为了实现某个功能会使用到其他类型的对象,这或多或少都存在对其他类型的依赖,为了降低这种依赖关系使用“依赖注入模式”获取对象实例是我们最佳的选择,但是这并不是唯一的选择,我们还可以使用“Service Locator”(服务器定位器模式)来提供对象并降低依赖关系。

1.差异

上述体现了两者的共同性,而两者也存在很大的差异性,下面我们在ASP.NET Core MVC的项目中通过代码层面看看二者的使用方式以及差异性。

Service Locator模式

 1   public class HomeController : Controller
 2     {
 3         private IComputer _computer;
 4 
 5         private readonly IServiceProvider _serviceProvider;
 6 
 7         public HomeController(IServiceProvider serviceProvider)
 8         {
 9             _serviceProvider = serviceProvider;
10         }
11 
12         public IActionResult Index()
13         {
14             this._computer = _serviceProvider.GetService<IComputer>();
15             ViewBag.Msg = _computer.SayHi();
16             return View();
17         }
18 
19     }

依赖注入模式

 1  public class HomeController : Controller
 2     {
 3         private IComputer _computer;
 4  
 5         public HomeController(IComputer computer)
 6         {
 7             _computer = computer;
 8         }
 9 
10         public IActionResult Index()
11         {
12             ViewBag.Msg = _computer.SayHi();
13             return View();
14         }
15 
16     }

1.1.代码上的差异

依赖注入和Service Locator实际上是针对同一事物(提供依赖对象),只不过根据自身不同的设计模式而定义为了不同的称谓。二者都是通过服务注册信息创建的容器来提供所需的服务实例,但是从上面代码可以看出二者在使用方式上还是存在很大的区别。

Service Locator(服务器定位器模式),主要是在应用程序代码中通过IServiceProvider容器,调用GetService方法来获取依赖的对象。而在依赖注入模式中,应用程序代码并没有参与对象的获取,而只是将依赖对象设置在构造函数的参数列表上,依赖的对象则是由框架提供。

在这个代码的使用层面上就可以分析出,依赖注入是实现了控制反转的,而Service Locator则没有实现。因为Service Locator模式对于对象创建的控制权还停留在应用程序代码中,而依赖注入则将这个控制权转移到了框架。

除了通过代码的使用方式分析出两者的差异,下面我们还可以根据,容器的使用者和对象获取形式来分析差异。

1.2.使用者的差异

依赖注入和Service Locator这两种设计模式,还可以根据对应容器使用者的角度来区分差异。

对于采用依赖注入模式的应用程序中,我们只需要预先完成所需服务的注册,并针对使用的类型提供构造函数的注入形式,框架在运行过程中会使用依赖注入容器为应用程序主动提供所需的服务实例。

而对于使用Service Locator模式的应用程序中,不但要完成所需服务的注册,自身需要在应用程序代码中调用容器的GetService方法来取得服务实例。

从容器使用者的角度来看,依赖注入容器的使用者应该是框架,而Service Locator容器的使用者则是应用程序。

1.3.主动和被动的差异

我们还可以从对象获取的角度来看出两者之间的差异。由于依赖注入是通过构造函数以“注入”的方式由框架主动提供依赖对象的,所以可以形象的比喻为,依赖注入是将对象“推送”的形式给到相应的类型中。Service Locator模式则是需要应用程序自身通过调用GetService方法“拉取”所需的依赖对象。这“一推一拉”也形象的体现出了应用程序在使用不同模式上,主动获取对象和被动获取对象的差异性。


2.反模式

微软在ASP.NET Core的官方文档中将Service Locator视为一种反模式,并明确看出了建议,要我们避免使用Service Locator(服务定位器模式),不要调用GetService方法来获取服务实例,而推荐使用依赖注入模式。

虽然微软在官方不建议使用Service Locator模式,但我个人认为这两种设计模式没有孰优孰劣之分,而是要看具体的应用场景和应用方式。关于这一点上,你可以在Martin Fowler发表的一篇文章中关于Service Locator模式部分进行深度研究,文章链接:https://www.martinfowler.com/articles/injection.html#ConcludingThoughts

在经过多方资料查阅,我个人认为微软在官方不建议使用Service Locator模式,主要有两点主要原因:

  1. 依赖关系被隐藏;
  2. 依赖未消除;

2.1.依赖关系被隐藏

使用依赖注入模式我们在应用程序当中可以很清楚的看到每个类型其中的依赖类型有哪些,由于采用构造函数注入的形式,我们在构造函数参数列表上就可以看到每个类型的依赖关系。

而对于使用Service Locator(服务定位器模式)的应用程序而言,你要查找类型的依赖关系,则只能在代码中搜索GetService方法的调用处来查看依赖关系有哪些类型。Service Locator模式显然不如依赖注入模式中查看构造函数那样方便,并且这种依赖关系被隐藏的情况很不易于我们进行编码开发工作。

2.2.依赖未消除

虽然使用Service Locator模式的GetService方法可以获取到依赖的对象,且避免了在类型中使用new方式创建对象带来的依赖性。但是这种方式只能视作一种“依赖的转移”,它并没有消除依赖,因为我们必须要通过容器对象的GetService方法才能获取对象,所以容器对象就相当于引入了一个新的依赖。

容器本质是相当于一个黑盒,对它的依赖不仅是模糊的也是不稳定的,如果容器发生变化,底层代码的改动会影响上层代码,那么我们的应用程序代码就必须做出相应的更改,这无疑也违法了依赖倒置原则。

 

posted @ 2022-08-03 17:04  姜承轩  阅读(511)  评论(0编辑  收藏  举报