依賴注入原理

來源:互聯網
上載者:User

轉: 依賴注入原理


依賴注入原理 發表於 2015-08-01    |   分類於 Software Engineering    |   3條評論 0. 前言

在軟體工程領域,依賴注入(Dependency Injection)是用於實現控制反轉(Inversion of Control)的最常見的方式之一。本文主要介紹依賴注入原理和常見的實現方式,重點在於介紹這種年輕的設計模式的適用情境及優勢。 1. 為什麼需要依賴注入

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

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

1234567891011121314151617
public class MovieLister {    private MovieFinder finder;    public MovieLister() {        finder = new MovieFinderImpl();    }        public Movie[] moviesDirectedBy(String arg) {        List allMovies = finder.findAll();        for (Iterator it = allMovies.iterator(); it.hasNext();) {            Movie movie = (Movie) it.next();            if (!movie.getDirector().equals(arg)) it.remove();        }        return (Movie[]) allMovies.toArray(new Movie[allMovies.size()]);    }    ...}

123
public interface MovieFinder {    List findAll();}

我們建立了一個名為MovieLister的類來提供需要的電影列表,它moviesDirectedBy方法提供根據導演名來搜尋電影的方式。真正負責搜尋電影的是實現了MovieFinder介面的MovieFinderImpl,我們的MovieLister類在建構函式中建立了一個MovieFinderImpl的對象。

目前看來,一切都不錯。但是,當我們希望修改finder,將finder替換為一種新的實現時(比如為MovieFinder增加一個參數表明Movie資料的來源是哪個資料庫),我們不僅需要修改MovieFinderImpl類,還需要修改我們MovieLister中建立MovieFinderImpl的代碼。

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

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

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

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

12345678
public class MovieLister {    private MovieFinder finder;    public MovieLister(MovieFinder finder) {        this.finder = finder;    }    ...}

2.2 setter注入

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

123456
public class MovieLister {    s...    public void setFinder(MovieFinder finder) {        this.finder = finder;    }}

2.3 介面注入

介面注入使用介面來提供setter方法,其實現方式如下。
首先要建立一個注入使用的介面。

123
public interface InjectFinder {    void injectFinder(MovieFinder finder);}

之後,我們讓MovieLister實現這個介面。
1234567
class MovieLister implements InjectFinder {    ...    public void injectFinder(MovieFinder finder) {      this.finder = finder;    }    ...}

最後,我們需要根據不同的架構建立被依賴的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 依賴注入

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.