相依性注入(Dependency Injection)是什麼?和壞壞的東西有關係嗎?

Dependency Injection

要是有在逛書局或是網路上亂看文章的話,大概都會常常會看到一個看起來很高大上的詞叫—相依性注入(Dependency Injection, DI)。下意識很容易覺得他和SQL Injection有關係,但讀了才發現根本沒有關聯。所以這次就來分享關於DI的概念吧。

基本上DI這個概念已經有超過十年歷史了,但還是很多人不太熟悉。如果設計程式時完全以直覺進行,可能就會如瀑布一般地從頭流到尾,想到什麼就做什麼。但如果不把程式重複或架構類似的地方提出來的話,不只會不斷地重複做一樣的事,也會導致測試變得很困難,也造成效率的浪費。因為每次都需要針對一點點小小的差異就要做程式的更改。這時候就有DI這個概念來拯救疲累的工程師們!他的使用方式有很多種,首先可以先談談他的用法。以ASP.Net Core的架構舉例,他的步驟大概是這樣:

 

  1. 建立Interface,宣告屬性、方法
  2. 建立Service繼承Interface
  3. 在Startup的Configuration中註冊該服務。這可以將類別實作和Interface對應起來
  4. 在Controller、Action或是View中注入服務的相依性物件。DI的框架會負責建立instance,以及在不需要的時候自動將服務放掉

 

這樣做的好處有什麼呢?首先因為已經定義了Interface,才去繼承。所以程式會直接和Interface黏結,而不是和特定的class有關。由於屬性和方法已經先被定義,形成共同標準。倘若要換服務的話也必須要以此標準來設計。如此一來,就達成了抽象化而避免了程式間的緊密耦合。也可以避免程式的使用場景有小更動時,就要重寫另一個版本。Configuration設定之類的東西也不會東奔西跑、四散各地。抽換服務也會變得容易很多。

 

那要特別注意的是,為了讓DI的設計不會化為烏有,要避免在Service中直接實例化(Instantiate)相依性類別或是使用靜態呼叫,這樣可能會導致某些部份變得過度緊密耦合。此外也要保持結構輕巧,以及便於測試的狀態。

 

在View這邊的使用DI時,要避免將前端與中後端混用。最好的狀況就是相依性的物件與資料都能從Controller傳入View。但是如果在僅涉及前端的邏輯時,就能使用。常見的狀況可能就是Service注入View中、透過注入的Service來改變UI、導入Configuration的資料到前端以及重新蓋掉先前注入的舊Service。

 

而在ASP.Net Core之下,服務的註冊可以選擇不同的生命週期。DI的生命週期分為以下三種,可以依照不同狀況,在註冊時選擇不同的模式。

 

  1. Trancient (短暫):使用AddTrancient註冊。每次向Service Container請求服務時,Service Instance就會被建立。在使用完畢後就會由DI Container來回收資源。適合用在輕量型的無狀態服務。
  2. Scoped (範圍):使用AddScoped註冊。在每個Client端得請求中。不管被請求呼叫幾次,都只會建立一次。
  3. Singleton (單一):使用AddSingleton註冊。當Configuration方法建立或是第一次被請求時,單一的Service Instance就會被建立。該服務會一直留存到可以讓後面的Client端都可以用到相同的Service Instance。

 

總結來說,DI可以解決程式間緊密耦合的問題,大大地減少了開發人員與測試人員重新撰寫程式的時間。而且在類似的場景之下,要抽換服務變得容易非常多。而且還可以選擇其生命週期、管理資源,讓開發與維持運作變得有條不紊,人人都是時間管理大師!


發佈留言

這個網站採用 Akismet 服務減少垃圾留言。進一步了解 Akismet 如何處理網站訪客的留言資料