入門 03 - 依賴注入DI
IoC模式基本上是一個高層的概念,在Martin Fowler的Inversion of Control Containers and the Dependency Injection pattern中談到,實現IoC有兩種方式:Dependency Injection與Service Locator。您可以在下面的網址中找到該篇文章:
http://www.martinfowler.com/articles/injection.html<?xml:namespace prefix = v ns = "urn:schemas-microsoft-com:vml" /><?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
Spring所採用的是Dependency Injection來實現IoC,中文翻譯爲依賴注入,依賴注入的意義是:「保留抽象接口,讓組件依賴於抽象接口,當組件要與其它實際的對象發生依賴關係時,藉過抽象接口來注入依賴的實際對象。」
看看下面這個程序:
public class BusinessObject {
private FloppyWriter writer = new FloppyWriter();
....
public void save() {
...
writer.saveToFloppy();
}
}
BusinessObject依賴於實際的FloppyWriter,爲了讓BusinessObject獲得重用性,我們不讓BusinessObject依賴於實際的FloppyWriter,而是依賴於抽象的接口:
public interface IDeviceWriter {
public void saveToDevice();
}
public class BusinessObject {
private IDeviceWriter writer;
public void setDeviceWriter(IDeviceWriter writer) {
this.writer = writer;
}
public void save() {
....
writer.saveToDevice();
}
}
public class FloppyWriter implement IDeviceWriter {
public void saveToDevice() {
....
// 實際儲存至Floppy的程序代碼
}
}
public class UsbDiskWriter implement IDeviceWriter {
public void saveToDevice() {
....
// 實際儲存至UsbDisk的程序代碼
}
}
如果今天BusinessObject想要與UseDiskWriter對象發生依賴關係,可以這麼建立:
businessObject.setDeviceWriter(new UsbDiskWriter());
由於BusinessObject依賴於抽象接口,在需要建立依賴關係時,我們可以透過抽象接口注入依賴的實際對象。
依賴注入在Martin Fowler的文章中談到了三種實現方式:interface injection、setter injection與constructor injection。並分別稱其爲type 1 IoC、type 2 IoC與type 3 IoC。
上面的BusinessObject所實現的是type 2 IoC,透過setter注入依賴關係,而type 3 IoC,則在是建構函式上注入依賴關係,例如:
public class BusinessObject {
private IDeviceWriter writer;
public BusinessObject(IDeviceWriter writer) {
this.writer = writer;
}
public void save() {
....
writer.saveToDevice();
}
}
Spring鼓勵的是setter injection,但也允許您使用constructor injection,使用setter或constructor來注入依賴關係視您的需求而定,使用constructor的好處之一是,您可以在建構物件的同時一併完成依賴關係的建立,然而如果要建立的對象關係很多,則會在建構函式上留下一長串的參數,這時使用setter會是個不錯的選擇,另一方面, setter可以有明確的名稱可以瞭解注入的對象會是什麼,像是setXXX()這樣的名稱會比記憶constructor上某個參數位置代表某個對象來得好。
Type 1 IoC是interface injection,使用type 1 IoC時會要求實作接口,這個接口是爲容器所用的,容器知道接口上所規定的方法,它可以呼叫實作接口的對象來完成依賴關係的注入,例如:
public interface IDependencyInjection {
public void createDependency(Map dependObjects);
}
public class BusinessObject implement IDependencyInjection {
private Map dependObjects;
public void createDependency(Map dependObjects) {
this.dependObject = dependObjects;
// 在這邊實現與BusinessObject的依賴關係
......
}
public void save() {
....
writer.saveToDevice();
}
}
如果要完成依賴關係注入的對象,必須實現IDependencyInjection接口,並交由容器管理,容器會呼叫被管理對象的createDependency()方法來完成依賴關係的建立。
在上面的例子中,type 1 IoC要求BusinessObject實現特定的接口,這就使得BusinessObject依賴於容器,如果日後BusinessObject要脫離目前這個容器,就必須修改程序,想想在更復雜的依賴關係中產生更多複雜的接口,組件與容器(框架)的依賴會更加複雜,最後使得組件無法從容器中脫離。
所以type 1 IoC具有強的侵入性,使用它來實現依賴注入會使得組件相依於容器(框架),降低組件的重用性。
Spring的核心是個IoC容器,您可以用setter或constructor的方式來實現您的業務對象,至於對象與對象之間的關係建立,則透過組態設定,讓Spring在執行時期根據組態檔的設定來爲您建立對象之間的依賴關係,您不必特地撰寫一些Helper來自行建立這些對象之間的依賴關係,這 不僅減少了大量的程序撰寫,也降低了對象之間的耦合程度。