天天看點

依賴注入 in 前端 && Typescript 實作依賴注入

最近因為工作需要,研究了vscode的代碼,看文檔時發現其中提到了Dependency Injection,并且在類的構造函數中看到了這樣的寫法。

因為對此依賴注入比較陌生,特地去了解了一番,故有此文章。

注意:此文章從前端角度說明依賴注入,非前端同學可以繞道

總結的來說,依賴注入是一種設計模式,因為它解決的是一類問題,這類問題是與依賴相關的。

要知道依賴注入是解決什麼問題,我們需要先了解一個原則:依賴倒轉原則。

這是設計模式的六大原則之一,其核心是面向接口程式設計。

它要求我們組織代碼時:

高層子產品不應該依賴低層子產品。兩個都應該依賴抽象

抽象不應該依賴細節

細節應該依賴抽象

意思是我們程式設計時,對系統進行子產品化,兩個子產品之間有依賴,例如子產品A依賴子產品B,那麼根據依賴倒轉原則,我們開發時,子產品A應該依賴子產品B的接口,而不是依賴子產品B的實作。

下圖描述了此原則提倡的關系:

依賴注入 in 前端 && Typescript 實作依賴注入
注意:雖然子產品A隻依賴接口程式設計,但在運作的時候,它還是需要有一個具體的子產品來負責子產品A需要的功能的,是以子產品A在【運作時】是需要一個【真的】子產品B,而不是它的接口。

是以上圖中,Module和Interface之間的線是包含,而不是關聯。

對前端來說,一般較少有抽象,如果不是使用Typescript的話,就更少接觸接口了。

但是,依賴注入卻是一直都存在,隻是許多同學沒有認出來而已。

看下面這個栗子,是前端普遍存在的依賴注入

簡單來說,依賴注入就做兩件事情

初始化被依賴的子產品

注入到依賴子產品中

再看一次栗子:

一般來說,在開發時,我們需要一個對象的能力,一般是自己去建立一個。

那如果,我需要很多個對象,而我需要的對象又依賴了其他對象,這時我是不是得對每個對象都建立一遍。

假設對象 A 依賴 B , C , D, 同時 B 又依賴 E。

如圖:

依賴注入 in 前端 && Typescript 實作依賴注入

建立一個a的執行個體:

可以看到,為了建立類A的一個執行個體,我們需要先建立 B,C,D 的執行個體,而為了建立一個B的執行個體,我們又需要建立一個E的執行個體。如果依賴的e子產品做了改變,構造時需要多傳入一個參數,那麼我們的代碼也得跟着改變,随着項目發展,工程越來越大,代碼将會變得非常不好維護。

這是我們很會自然想到,有沒方法使依賴子產品與被依賴子產品的初始化資訊解耦。

其實,js實作依賴注入的方式有多種,簡單列舉一些:

基于Injector、Cache和函數參數名的依賴注入

AngularJS中基于雙Injector的依賴注入

inversify.js——Javascript技術棧中的IoC容器

TypeScript中基于裝飾器和反射的依賴注入

......

那現在我用在工作中使用的模式來講,也是基于Typescript的裝飾器,但不需要用到反射。

先看下如果使用了依賴注入後,子產品A是如何使用其他子產品的。

是不是很簡單明了,以後使用A子產品的能力時,隻需要執行個體化A(執行個體化時需要使用封裝的方法),就不需要管他依賴的參數了。

那麼這是如何實作的呢,下面來看下具體方式。

首先,每個類都需要依照自己的接口來實作,接口需要傳入createDecorator,此方法會傳回一個裝飾器,以B子產品為例:

依賴注入(Dependency Injection),是這樣一個過程:由于某客戶類隻依賴于服務類的一個接口,而不依賴于具體服務類,是以客戶類隻定義一個注入點。在程式運作過程中,客戶類不直接執行個體化具體服務類執行個體,而是客戶類的運作上下文環境或專門元件負責執行個體化服務類,然後将其注入到客戶類中,保證客戶類的正常運作。

參考文章:

<a href="http://www.cnblogs.com/leoo2sk/archive/2009/06/17/1504693.html">依賴注入那些事兒</a>

<a href="http://imweb.io/topic/571b567505637d4c67ae3f64">前端需要知道的 依賴注入(Dependency Injection, DI)</a>

繼續閱讀