天天看點

依賴注入

0. 前言

在軟體工程領域,依賴注入(Dependency Injection)是用于實作控制反轉(Inversion of Control)的最常見的方式之一。本文主要介紹依賴注入原理和常見的實作方式,重點在于介紹這種年輕的設計模式的适用場景及優勢。

1. 為什麼需要依賴注入

控制反轉用于解耦,解的究竟是誰和誰的耦?這是我在最初了解依賴注入時候産生的第一個問題。

下面我引用Martin Flower在解釋介紹注入時使用的一部分代碼來說明這個問題。

目前看來,一切都不錯。但是,當我們希望修改finder,将finder替換為一種新的實作時(比如為MovieFinder增加一個參數表明Movie資料的來源是哪個資料庫),我們不僅需要修改MovieFinderImpl類,還需要修改我們MovieLister中建立MovieFinderImpl的代碼。我們建立了一個名為MovieLister的類來提供需要的電影清單,它moviesDirectedBy方法提供根據導演名來搜尋電影的方式。真正負責搜尋電影的是實作了MovieFinder接口的MovieFinderImpl,我們的MovieLister類在構造函數中建立了一個MovieFinderImpl的對象。

這就是依賴注入要處理的耦合。這種在MovieLister中建立MovieFinderImpl的方式,使得MovieLister不僅僅依賴于MovieFinder這個接口,它還依賴于MovieListImpl這個實作。 這種在一個類中直接建立另一個類的對象的代碼,和寫死(hard-coded strings)以及寫死的數字(magic numbers)一樣,是一種導緻耦合的壞味道,我們可以把這種壞味道稱為硬初始化(hard init)。同時,我們也應該像記住寫死一樣記住,new(對象建立)是有毒的。

Hard Init帶來的主要壞處有兩個方面:1)上文所述的修改其實作時,需要修改建立處的代碼;2)不便于測試,這種方式建立的類(上文中的MovieLister)無法單獨被測試,其行為和MovieFinderImpl緊緊耦合在一起,同時,也會導緻代碼的可讀性問題(“如果一段代碼不便于測試,那麼它一定不便于閱讀。”)。

2. 依賴注入的實作方式

依賴注入其實并不神奇,我們日常的代碼中很多都用到了依賴注入,但很少注意到它,也很少主動使用依賴注入進行解耦。這裡我們簡單介紹一下賴注入實作三種的方式。

這是我認為的最簡單的依賴注入方式,我們修改一下上面代碼中MovieList的構造函數,使得MovieFinderImpl的實作在MovieLister類之外建立。這樣,MovieLister就隻依賴于我們定義的MovieFinder接口,而不依賴于MovieFinder的實作了。

類似的,我們可以增加一個setter函數來傳入建立好的MovieFinder對象,這樣同樣可以避免在MovieFinder中hard init這個對象。

接口注入使用接口來提供setter方法,其實作方式如下。

首先要建立一個注入使用的接口。

之後,我們讓MovieLister實作這個接口。

最後,我們需要根據不同的架構建立被依賴的MovieFinder的實作。

3. 最後

依賴注入降低了依賴和被依賴類型間的耦合,在修改被依賴的類型實作時,不需要修改依賴類型的實作,同時,對于依賴類型的測試,可以更友善的使用mocking object替代原有的被依賴類型,以達到對依賴對象獨立進行單元測試的目的。

最後需要注意的是,依賴注入隻是控制反轉的一種實作方式。控制反轉還有一種常見的實作方式稱為依賴查找。

參考

​​Inversion of Control Containers and the Dependency Injection pattern​​

​​How to Think About the “new” Operator with Respect to Unit Testing​​

​​依賴注入​​

聯系方式:emhhbmdfbGlhbmcxOTkxQDEyNi5jb20=

上一篇: 依賴注入