跳轉到內容

基於元件的開發/DI

來自華夏公益教科書

為了弄清依賴注入 (DI) 的作用,首先需要描述的是它所指的依賴 (Dependency) 的型別。

在 DI 中,D 代表類之間或更準確地說,對應物件之間的依賴關係。

通常,較複雜的物件需要其他物件的某些功能,因此,這些物件儲存在成員變數 (物件變數) 中。例如 (摘自 M. Fowler:控制反轉容器和依賴注入模式,2004 http://martinfowler.com/articles/injection.html

..
public class MovieLister {
  ...
  private MovieFinder ;
  ....
  
  public Movie[] moviesDirectedBy(String directorName) {
        List<Movie> allMovies = finder.findAll();
        for (Movie movie : allMovies){
            if (!movie.getDirector().equals(directorName)) {
               it.remove();
            }
        }
        return allMovies.toArray();
    }
}

方法 moviesDirectedBy 使用了一個型別為 MovieFinder 的物件。人們會說 MovieFinder **依賴於** MovieLister

MovieFinder 是一個介面,它可以有多種實現(DBMovieFinder, ColonDelimiterMovieFinder)。

public interface MovieFinder {
  List<Movie> findAll();
}


但是,如果直接在 MovieLister 的建構函式中建立具體的 MovieFinder 例項,則這種可複用性將無法實現,例如:

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

MovieListerMovieFinder 的一種實現 (ColonDelimitedMovieFinder) 緊密繫結。這會導致強耦合。因此,方法 moviesDirectedByMovieLister 將無法使用 MovieFinder 的所有可能的形式(實現)。此外,在沒有同時測試 ColonDelimitedMovieFinder 的情況下,無法測試方法 moviesDirectedBy。另一個缺點是,MovieListerMovieFinder 的依賴關係在方法簽名(想想 Javadoc)中不可見,而只能在實現中可見。

解決這個問題的一個方法是,將依賴關係從外部注入到物件中,例如透過建構函式(建構函式注入)。不是類本身負責解析使用的具體介面實現,而是從外部注入。因此命名為“依賴 (Dependency) 注入 (Injection)”。


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

這裡,從建構函式簽名可以看出依賴關係。


這是依賴注入 (DI) 的基礎。這樣做有以下優點:

  • 更好的可複用性
  • 減弱元件之間的耦合
  • 可以進行隔離測試(注入模擬物件)
  • 在方法或建構函式簽名中識別依賴關係。
華夏公益教科書