iT邦幫忙

2

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 物件" 的時候, 就要一併傳入所有相依物件,
對呼叫端來說相當明確直覺, 馬上可以得知這個物件相依於哪些第三方物件.


圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言