什么是IOC呢,在網(wǎng)上搜到了一非常有意思的講解。
IoC就是Inversion of Control,控制反轉(zhuǎn)。在Java開發(fā)中,IoC意味著將你設(shè)計(jì)好的類交給系統(tǒng)去控制,而不是在你的類內(nèi)部控制。這稱為控制反轉(zhuǎn)。
下面我們以幾個(gè)例子來說明什么是IoC
假設(shè)我們要設(shè)計(jì)一個(gè)Girl和一個(gè)Boy類,其中Girl有kiss方法,即Girl想要Kiss一個(gè)Boy。那么,我們的問題是,Girl如何能夠認(rèn)識(shí)這個(gè)Boy?
在我們中國,常見的MM與GG的認(rèn)識(shí)方式有以下幾種
1 青梅竹馬; 2 親友介紹; 3 父母包辦
那么哪一種才是最好呢?
青梅竹馬:Girl從小就知道自己的Boy。
1: public class Girl {
2: void kiss(){
3: Boy boy = new Boy();
4: }
5: }
然而從開始就創(chuàng)建的Boy缺點(diǎn)就是無法在更換。并且要負(fù)責(zé)Boy的整個(gè)生命周期。如果我們的Girl想要換一個(gè)怎么辦?(嚴(yán)重不支持Girl經(jīng)常更換Boy,#_#)
親友介紹:由中間人負(fù)責(zé)提供Boy來見面
1: public class Girl {
2: void kiss(){
3: Boy boy = BoyFactory.createBoy();
4: }
5: }
親友介紹,固然是好。如果不滿意,盡管另外換一個(gè)好了。但是,親友BoyFactory經(jīng)常是以Singleton的形式出現(xiàn),不然就是,存在于Globals,無處不在,無處不能。實(shí)在是太繁瑣了一點(diǎn),不夠靈活。我為什么一定要這個(gè)親友摻和進(jìn)來呢?為什么一定要付給她介紹費(fèi)呢?萬一最好的朋友愛上了我的男朋友呢?
父母包辦:一切交給父母,自己不用費(fèi)吹灰之力,只需要等著Kiss就好了。
1: public class Girl {
2: void kiss(Boy boy){
3: // kiss boy
4: boy.kiss();
5: }
6: }
Well,這是對Girl最好的方法,只要想辦法賄賂了Girl的父母,并把Boy交給他。那么我們就可以輕松的和Girl來Kiss了。看來幾千年傳統(tǒng)的父母之命還真是有用哦。至少Boy和Girl不用自己瞎忙乎了。
這就是IOC,將對象的創(chuàng)建和獲取提取到外部。由外部容器提供需要的組件。
我們知道好萊塢原則:“Do not call us, we will call you.” 意思就是,You, girlie, do not call the boy. We will feed you a boy。
我們還應(yīng)該知道依賴倒轉(zhuǎn)原則即 Dependence Inversion Princinple,DIP
Eric Gamma說,要面向抽象編程。面向接口編程是面向?qū)ο蟮暮诵摹?
組件應(yīng)該分為兩部分,即 Service, 所提供功能的聲明 Implementation, Service的實(shí)現(xiàn)
好處是:多實(shí)現(xiàn)可以任意切換,防止 “everything depends on everything” 問題.即具體依賴于具體。
所以,我們的Boy應(yīng)該是實(shí)現(xiàn)Kissable接口。這樣一旦Girl不想kiss可惡的Boy的話,還可以kiss可愛的kitten和慈祥的grandmother。
二、IOC的type
IoC的Type指的是Girl得到Boy的幾種不同方式。我們逐一來說明。
IOC type 0:不用IOC
1: public class Girl implements Servicable {
2: private Kissable kissable;
3: public Girl() {
4: kissable = new Boy();
5: }
6: public void kissYourKissable() {
7: kissable.kiss();
8: }
9: }
Girl自己建立自己的Boy,很難更換,很難共享給別人,只能單獨(dú)使用,并負(fù)責(zé)完全的生命周期。
IOC type 1,先看代碼:代碼
1: public class Girl implements Servicable {
2:
3: Kissable kissable;
4:
5: public void service(ServiceManager mgr) {
6: kissable = (Kissable) mgr.lookup(“kissable”);
7: }
8:
9: public void kissYourKissable() {
10: kissable.kiss();
11: }
12: }
這種情況出現(xiàn)于Avalon Framework。一個(gè)組件實(shí)現(xiàn)了Servicable接口,就必須實(shí)現(xiàn)service方法,并傳入一個(gè)ServiceManager。其中會(huì)含有需要的其它組件。只需要在service方法中初始化需要的Boy。
另外,J2EE中從Context取得對象也屬于type 1。它依賴于配置文件。
IOC type 2:
1: public class Girl {
2:
3: private Kissable kissable;
4:
5: public void setKissable(Kissable kissable) {
6: this.kissable = kissable;
7: }
8:
9: public void kissYourKissable() {
10: kissable.kiss();
11: }
12: }
Type 2出現(xiàn)于Spring Framework,是通過JavaBean的set方法來將需要的Boy傳遞給Girl。它必須依賴于配置文件。
IOC type 3:
1: public class Girl {
2:
3: private Kissable kissable;
4:
5: public Girl(Kissable kissable) {
6: this.kissable = kissable;
7: }
8:
9: public void kissYourKissable() {
10: kissable.kiss();
11: }
12: }
13:
這就是PicoContainer的組件 。通過構(gòu)造函數(shù)傳遞Boy給Girl
PicoContainer container = new DefaultPicoContainer();
container.registerComponentImplementation(Boy.class);
container.registerComponentImplementation(Girl.class);
Girl girl = (Girl) container.getComponentInstance(Girl.class);
girl.kissYourKissable();
參考資料
1 http://www.picocontainer.org/presentations/JavaPolis2003.ppt
http://www.picocontainer.org/presentations/JavaPolis2003.pdf
2 DIP, Robert C Martin, Bob大叔的優(yōu)秀論文
http://www.objectmentor.com/resources/articles/dip.pdf
3 Dependency Injection 依賴注射,Matrin Fowler對DIP的擴(kuò)展
http://www.martinfowler.com/articles/injection.html
4 IOC框架
PicoContainer 優(yōu)秀的IOC框架
http://picocontainer.org/
Avalon
http://avalon.apache.org/
Spring Framework
http://www.springframework.org/
HiveMind
http://jakarta.apache.org/commons/hivemind
這篇短文基本上是改編自Martin Fowler的Inversion of Control Containers and the Dependency Injection pattern,目的呢,是讓讀者能夠在最短時(shí)間內(nèi)了解IoC的概念。這也是我一貫的“風(fēng)格”:最短的文字、最精要的內(nèi)容、最清晰的說明。希望我能做到,自勉^_^
在J2EE應(yīng)用開發(fā)中,經(jīng)常遇到的問題就是:如何將不同的組件組裝成為一個(gè)內(nèi)聚的應(yīng)用程序?IoC模式可以解決這個(gè)問題,其目標(biāo)是將組件的配置與使用分離開。
IoC,Inversion of Control,控制反轉(zhuǎn)[1],其原理是基于OO設(shè)計(jì)原則的The Hollywood Principle:Don't call us, we'll call you。也就是說,所有的組件[2]都是被動(dòng)的(Passive),所有的組件初始化和調(diào)用都由容器負(fù)責(zé)。組件處在一個(gè)容器當(dāng)中,由容器負(fù)責(zé)管理。
要說明IoC模式最好的方法是使用代碼。下邊是一段正常的代碼。
1: class ClassA...
2:
3: public String aMethod(String arg){
4:
5: String result = instanceOfClassB.bMethod();
6:
7: do something;
8:
9: return result;
10:
11: }
在上邊的代碼里,我們要解決的問題是:ClassA如何獲得ClassB的實(shí)例?一個(gè)最直接的方法是在aMethod里聲明:
IClassB instanceOfClassB = new ClassB();
這里使用了一個(gè)接口IClassB。
問題是,如果出現(xiàn)這樣的情況:繼續(xù)使用ClassA,但要求用IClassB的另一個(gè)實(shí)現(xiàn)ClassB2代替ClassB呢?更概括一點(diǎn)說:ClassA怎樣才能找到IClassB的具體實(shí)現(xiàn)?很明顯,上述代碼增加ClassA和ClassB的耦合度,以致于無法在不修改ClassA的情況下變更IClassB的具體實(shí)現(xiàn)。
IoC模式就是用于解決這樣的問題。當(dāng)然,還有其他的方法,比如Service Locator模式,但現(xiàn)在我們只關(guān)注IoC。如前所述,IoC容器負(fù)責(zé)初始化組件(如IClassB),并將實(shí)例交給使用者。使用代碼或配置文件以聲明的方式將接口與實(shí)例關(guān)聯(lián)起來,IoC容器負(fù)責(zé)進(jìn)行實(shí)際的調(diào)用處理。對于調(diào)用者,只需要關(guān)注接口就行了。
根據(jù)實(shí)例傳入方式的不同,IoC分為type 1 IoC(接口注入[3])、type 2 IoC(設(shè)值方法注入)和type 3 IoC(構(gòu)造子注入)。分別用代碼說明如下:
type 1 IoC(接口注入)
1: public interface GetClassB {
2:
3: void getClassB(IClassB instanceOfClassB);
4:
5: }
6:
7: class ClassA implements GetClassB…
8:
9: IClassB instanceOfClassB;
10:
11: void getClassB(IClassB instanceOfClassB) {
12:
13: this.instanceOfClassB = instanceOfClassB;
14:
15: }
type 2 IoC(設(shè)值方法注入)
1: class ClassA...
2:
3: IClassB instanceOfClassB;
4:
5: public void setFinder(IClassB instanceOfClassB) {
6:
7: this.instanceOfClassB = instanceOfClassB;
8:
9: }
type 3 IoC(構(gòu)造子注入)
1: class ClassA…
2:
3: ClassB instanceOfClassB;
4:
5: public classA(IClassB instanceOfClassB) {
6:
7: this. instanceOfClassB = instanceOfClassB;
8:
9: }
Spring使用的是type 2 IoC。
[1] 在Martin Fowler的Inversion of Control Containers and the Dependency Injection pattern一文中,作者提出本模式更準(zhǔn)確的名稱應(yīng)該是Dependency Injection。考慮到使用上的習(xí)慣,在本文檔中我們將繼續(xù)使用IoC的概念。
[2] 同一篇文章中,Martin Fowler還提出了Componet(組件)與Service(服務(wù))的區(qū)別問題。我個(gè)人認(rèn)為“組件”更有助于幫助我們理解IoC的概念,所以在本文檔中使用“組件”來代表“組件或服務(wù)”
[3] 如上所述,注入是另一種說法,此處只用于輔助說明。