1. 程式人生 > >手把手教你寫DI_0_DI是什麼?

手把手教你寫DI_0_DI是什麼?

DI是什麼?

Dependency Injection 常常簡稱為:DI。

它是實現控制反轉(Inversion of Control – IoC)的一個模式。

fowler 大大大神 “幾十年”前的經典文章 https://www.martinfowler.com/articles/injection.html 說的很清楚。

“幾十年”以來,相信大家都早已學會了 大大大神 的教典。

我們簡單回憶一下對應內容,以便我們可以順利進入後續章節:徒手擼個小DI。

文章內容大致是這樣:

首先舉例:


public interface MovieFinder {
    List findAll();
}

class MovieLister {

    private MovieFinder finder;

   public MovieLister() {
    finder = new ColonDelimitedMovieFinder("movies1.txt");
  }

  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()]);
  }

然後大大大神吐槽了一堆:

這個實現類的名字就說明:我將要從一個逗號分隔的文字檔案中獲得影片列表。你不必操心具體的實現細節,只要設想這樣一個實現類就可以了。如果這個類只由我自己使用,一切都沒問題。但是,如果我的朋友歎服於這個精彩的功能,也想使用我的程式,那又會怎麼樣呢?如果他們也把影片清單儲存在一個逗號分隔的文字檔案中,並且也把這個檔案命名為” movie1.txt “,那麼一切還是沒問題。如果他們只是給這個檔案改改名,我也可以從一個配置檔案獲得檔名,這也很容易。但是,如果他們用完全不同的方式——例如SQL 資料庫、XML 檔案、web service,或者另一種格式的文字檔案——來儲存影片清單呢?在這種情況下,我們需要用另一個類來獲取資料。由於已經定義了MovieFinder介面,我可以不用修改moviesDirectedBy方法。但是,我仍然需要通過某種途徑獲得合適的MovieFinder實現類的例項。

還有張依賴圖

818422-20181106222049996-1554441994.png

MovieLister類既依賴於MovieFinder介面,也依賴於具體的實現類。我們當然希望MovieLister類只依賴於介面,但我們要如何獲得一個MovieFinder子類的例項呢?

在Patterns of Enterprise Application Architecture一書中,我們把這種情況稱為外掛(plugin):MovieFinder的實現類不是在編譯期連入程式之中的,因為我並不知道我的朋友會使用哪個實現類。我們希望MovieLister類能夠與MovieFinder的任何實現類配合工作,並且允許在執行期插入具體的實現類,插入動作完全脫離我(原作者)的控制。這裡的問題就是:如何設計這個連線過程,使MovieLister類在不知道實現類細節的前提下與其例項協同工作。

將這個例子推而廣之,在一個真實的系統中,我們可能有數十個服務和元件。在任何時候,我們總可以對使用元件的情形加以抽象,通過介面與具體的元件交流(如果元件並沒有設計一個介面,也可以通過介面卡與之交流)。但是,如果我們希望以不同的方式部署這個系統,就需要用外掛機制來處理服務之間的互動過程,這樣我們才可能在不同的部署方案中使用不同的實現。所以,現在的核心問題就是:如何將這些外掛組合成一個應用程式?這正是新生的輕量級容器所面臨的主要問題,而它們解決這個問題的手段無一例外地是控制反轉(Inversion of Control)模式。

學術一點就是說 避免類之間強耦合,我們需要用依賴注入等方式在執行時才建立依賴達到程式碼鬆耦合,從而使程式碼易為維護

戲言就是在說:

  1. 我們都是大忙人,請你作為一個類簡單明瞭的說清楚 : 你這個類能幹什麼事? 不要讓我們這些大忙人把你每件衣服一件一件看完了才知道你是木匠, 還是鐵匠
  2. 我們都是大老闆,我們財產不能全靠你一個,你不能幹活或者你幹不好活,我們做老闆的人必須能找人換了你

所以上述程式碼中:

我(MovieLister)離不開了 你 (ColonDelimitedMovieFinder("movies1.txt")),

但是我們男人必須靠自己,至少表面沒人看出我們之間的關係

只有從我們(MovieLister)身體裡面沒有了你,才能沒人看出我們之間的關係

當我們開始幹活的時候,我們再根據我們的私下關係協調好工作,男女搭配,好好幹活。

說到這裡, 各位要被面試的同學記好這些話, 不要被問到依賴注入幫我解決了什麼事情的時候, 回一句 我們不用自己new 物件啦, 這樣大家就不會看見面試官無語又懵逼的臉了。

依賴注入的幾種形式

  • 建構函式注入
class MovieLister 
{
   private IMovieFinder finder;

   public MovieLister(IMovieFinder finder) {
    this.finder = finder;
  }
}
  • 屬性注入
class MovieLister 
{
   [Inject]
   public IMovieFinder Finder { get; set;}
}
  • 介面注入
public interface InjectFinder
{
    void injectFinder(MovieFinder finder);
}
class MovieLister : InjectFinder 
{
    private IMovieFinder finder;

    public void injectFinder(MovieFinder finder)
    {
        this.finder = finder;
    }
}

這幾種方式之間並沒有效能或者什麼特別的優勢,主要是形式上的差異。

具體對比可以參考 http://insights.thoughtworkers.org/injection/

下一章: 做一個支援建構函式注入的ioc容器

引用參考:

  • http://insights.thoughtworkers.org/injection/
  • https://www.martinfowler.com/articles/injection.html