DI(Dependency injection) 注入方式

DI(Dependency injection) 注入方式

这天小明问说

他接手维护的专案中, 从头到尾都一律用 DI 属性注入, 这是很好的设计方式吗?

首先我先简单说明一下, DI 的核心概念是宽鬆耦合, DI 有三种注入的方式:

建构式注入(Constructor Injection)属性注入(Property Injection)方法注入(Method Injection)

建构式注入(Constructor Injection)

public class MyHome {   IMailService _mailService;   public MyHome(IMailService mailService) {      _mailService = mailService;   }}

以上程式码示範了建构式注入, 当你手动 "new 一个物件" 就必须把 IMailService 物件带进来.
因此该物件在没有这些依赖物件时无法被建立.

属性注入(Property Injection)

public class MyHome {   public MyHome() {   }    public IMailService MailService { get; set; }}

以上程式码示範属性注入, 当你手动 "new 一个物件", 不必一开始就把 IMailService 物件带进来.
这意味着你的物件可以在没有提供这些依赖时正常地工作.

原则上这种方式, 对于不注入相依物件的情况, 物件本身必须做些防护措施, 以免物件执行的时候, 因相依物件参考为 null 而引发 NullReferenceException 异常.

方法注入(Method Injection)

public class MyHome {   public MyHome() {   }    public void Run(IMailService MailService) {      ...   }}

以上程式码示範方法注入, 当你手动 "new 一个物件", 跟属性注入一样, 也不必一开始就把 IMailService 物件带进来. 只有在用户端呼叫 Run 方法时才需要传入 IMailService 相依物件.


现在我们了解三种注入的方式跟特性, 回头看小明的问题, 他接手的专案中 "一律都用属性注入" ,
以下是小明接手专案中的程式码片段

public class MyHome {   public IMailService MailService { get; set; }   public Ixxx1 xxx1 { get; set; }   public Ixxx2 xxx2 { get; set; }   public IxxxN xxxN { get; set; }   public void Run() {      MailService.Call();      ...   }}

以上程式码有几个问题:

对于这个 MyHome 物件採用属性注入, 因为它让依赖不明确, 这意味着在建立物件期间不可能容易地看出依赖关係.
这对单元测试来说很重要, 因为你可能想要模拟一些依赖物件.而且 Run 方法里面直接呼叫 MailService 依赖物件, 但 MyHome 被建立的时候, 也没有预设依赖物件的实作.
这会引发 NullReferenceException 异常

另外小明的专案交接人也有疑问说:

那我把所有属性注入通通改成建构式注入, 但是依赖物件超级多, 我不想在建构式看到一堆参数, 所以我才要把这些一堆依赖物件通通改成属性注入阿

遇到这种问题,
我们就应该思考 "当这个物件的建构式需要的参数数量很多的时候",
可能是一种 "程式码坏味道" (Code Smell),
表明您的物件做太多事情, 可能没有遵循单一职责原则.
请考虑将程式码重构为许多相互消耗的较小物件.

结尾

每当需要注入相依物件时, 建议优先考虑 "建构式注入" ,
因为 "new 物件" 的时候, 就要一併传入所有相依物件,
对呼叫端来说相当明确直觉, 马上可以得知这个物件相依于哪些第三方物件.


关于作者: 网站小编

码农网专注IT技术教程资源分享平台,学习资源下载网站,58码农网包含计算机技术、网站程序源码下载、编程技术论坛、互联网资源下载等产品服务,提供原创、优质、完整内容的专业码农交流分享平台。

热门文章