基於元件的開發/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");
}
MovieLister 與 MovieFinder 的一種實現 (ColonDelimitedMovieFinder) 緊密繫結。這會導致強耦合。因此,方法 moviesDirectedBy 或 MovieLister 將無法使用 MovieFinder 的所有可能的形式(實現)。此外,在沒有同時測試 ColonDelimitedMovieFinder 的情況下,無法測試方法 moviesDirectedBy。另一個缺點是,MovieLister 到 MovieFinder 的依賴關係在方法簽名(想想 Javadoc)中不可見,而只能在實現中可見。
解決這個問題的一個方法是,將依賴關係從外部注入到物件中,例如透過建構函式(建構函式注入)。不是類本身負責解析使用的具體介面實現,而是從外部注入。因此命名為“依賴 (Dependency) 注入 (Injection)”。
public MovieLister(MovieFinder finder) {
this.finder = finder;
}
這裡,從建構函式簽名可以看出依賴關係。
這是依賴注入 (DI) 的基礎。這樣做有以下優點:
- 更好的可複用性
- 減弱元件之間的耦合
- 可以進行隔離測試(注入模擬物件)
- 在方法或建構函式簽名中識別依賴關係。