首頁  >  文章  >  後端開發  >  php為什麼要用依賴注入?

php為什麼要用依賴注入?

藏色散人
藏色散人原創
2019-05-18 13:19:363504瀏覽

因為依賴注入會降低依賴和被依賴類型間的耦合,在修改被依賴的類型實作時,不需要修改依賴類型的實作;同時,對於依賴類型的測試,可以更方便的使用「mocking object」取代原有的被依賴類型,以達到對依賴物件獨立進行單元測試的目的。

php為什麼要用依賴注入?

0. 前言

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

1. 為什麼需要依賴注入

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

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

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()]);
    }
    ...
}
public interface MovieFinder {
    List findAll();
}

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

目前看來,一切都不錯。但是,當我們希望修改find​​er,將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的實作了。

public class MovieLister {
    private MovieFinder finder;

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

2.2 setter注入

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

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

2.3 介面注入

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

首先要建立一個注入使用的介面。

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

之後,我們讓MovieLister實作這個介面。

class MovieLister implements InjectFinder {
    ...
    public void injectFinder(MovieFinder finder) {
      this.finder = finder;
    }
    ...
}

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

3. 最後

依賴注入降低了依賴和被依賴型別間的耦合,在修改被依賴的型別實作時,不需要修改依賴型別的實現,同時,對於依賴類型的測試,可以更方便的使用mocking object取代原有的被依賴類型,以達到對依賴物件獨立進行單元測試的目的。

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

以上是php為什麼要用依賴注入?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn