1.依賴在哪里
老馬舉了一個小例子,是開發一個電影列舉器(MovieList),這個電影列舉器需要使用一個電影查找器(MovieFinder)提供的服務,偽碼如下:
1
/**//*服務的接口*/
2
public interface MovieFinder
{
3
ArrayList findAll();
4
}
5
6
/**//*服務的消費者*/
7
class MovieLister
8

{
9
public Movie[] moviesDirectedBy(String arg)
{
10
List allMovies = finder.findAll();
11
for (Iterator it = allMovies.iterator(); it.hasNext();)
{
12
Movie movie = (Movie) it.next();
13
if (!movie.getDirector().equals(arg)) it.remove();
14
}
15
return (Movie[]) allMovies.toArray(new Movie[allMovies.size()]);
16
}
17
18
/**//*消費者內部包含一個將指向具體服務類型的實體對象*/
19
private MovieFinder finder;
20
/**//*消費者需要在某一個時刻去實例化具體的服務。這是我們要解耦的關鍵所在,
21
*因為這樣的處理方式造成了服務消費者和服務提供者的強耦合關系(這種耦合是在編譯期就確定下來的)。
22
**/
23
public MovieLister()
{
24
finder = new ColonDelimitedMovieFinder("movies1.txt");
25
}
26
}2.DI的實現方式
和上面的圖1對應的是,如果我們的系統實現了依賴注入,組件間的依賴關系就變成了圖2:
圖2
說白了,就是要提供一個容器,由容器來完成(1)具體ServiceProvider的創建(2)ServiceUser和ServiceProvider的運行時綁定。下面我們就依次來看一下三種典型的依賴注入方式的實現。特別要說明的是,要理解依賴注入的機制,關鍵是理解容器的實現方式。本文后面給出的容器參考實現,均為黃忠成老師的代碼,筆者僅在其中加上了一些關鍵注釋而已。
2.1 Constructor Injection(構造器注入)
我們可以看到,在整個依賴注入的數據結構中,涉及到的重要的類型就是ServiceUser, ServiceProvider和Assembler三者,而這里所說的構造器,指的是ServiceUser的構造器。也就是說,在構造ServiceUser實例的時候,才把真正的ServiceProvider傳給他:
1
class MovieLister
2

{
3
//其他內容,省略
4
5
public MovieLister(MovieFinder finder)
6
{
7
this.finder = finder;
8
}
9
}
2.2 Setter Injection(設值注入)
這種注入方式和構造注入實在很類似,唯一的區別就是前者在構造函數的調用過程中進行注入,而它是通過給屬性賦值來進行注入。無怪乎PicoContainer和Spring都是同時支持這兩種注入方式。Spring對通過XML進行配置有比較好的支持,也使得Spring中更常使用設值注入的方式:
1
<beans>
2
<bean id="MovieLister" class="spring.MovieLister">
3
<property name="finder">
4
<ref local="MovieFinder"/>
5
property>
6
bean>
7
<bean id="MovieFinder" class="spring.ColonMovieFinder">
8
<property name="filename">
9
<value>movies1.txtvalue>
10
property>
11
bean>
12
beans>2.4 除了DI,還有Service Locator
上面提到的依賴注入只是消除ServiceUser和ServiceProvider之間的依賴關系的一種方法,還有另一種方法:服務定位器(Service Locator)。也就是說,由ServiceLocator來專門負責提供具體的ServiceProvider。當然,這樣的話ServiceUser不僅要依賴于服務的接口,還依賴于ServiceContract。仍然是最早提到過的電影列舉器的例子,如果使用Service Locator來解除依賴的話,整個依賴關系應當如下圖所示:
圖3
用起來也很簡單,在一個適當的位置(比如在一組相關服務即將被調用之前)對ServiceLocator進行初始化,用到的時候就直接用ServiceLocator返回ServiceProvider實例:
1
//服務定位器的初始化
2
ServiceLocator locator = new ServiceLocator();
3
locator.loadService("MovieFinder", new ColonMovieFinder("movies1.txt"));
4
ServiceLocator.load(locator);
5
//服務定義器的使用
6
//其實這個使用方式體現了服務定位器和依賴注入模式的最大差別:ServiceUser需要顯示的調用ServiceLocator,從而獲取自己需要的服務對象;
7
//而依賴注入則是隱式的由容器完成了這一切。
8
MovieFinder finder = (MovieFinder) ServiceLocator.getService("MovieFinder");
9
it知識庫:深度理解依賴注入,轉載需保留來源!
鄭重聲明:本文版權歸原作者所有,轉載文章僅為傳播更多信息之目的,如作者信息標記有誤,請第一時間聯系我們修改或刪除,多謝。