控制反轉(Ioc)的設計原則
--減輕組件間的依賴(lài)性及藕合性的設計原則
作者:Make 創(chuàng )建日期:2005-07-24
導讀
1.緣由
2、回顧面向對象思想
3.什么是Ioc
4、現行的幾種實(shí)現方式
5.結論
6.附錄
一、緣由
“萬(wàn)事萬(wàn)物皆有姻緣”,這一句話(huà)本是佛家偕語(yǔ)。不過(guò)筆者認為,這一句話(huà)也真是道出了世間萬(wàn)事萬(wàn)物的相輔相成的最徹底的一句解釋。在IT這一行,更新?lián)Q代比光速還快,我想同仁們都不會(huì )反駁我這句話(huà)吧!要不試試,從藝術(shù)性的工作,到工程化的項目,從面向過(guò)程到面向對象,從面向對象到面向方面!每一種思想都是那么的偉大,每一種思想都沒(méi)有任何一個(gè)人(還真是任何一個(gè)人,不信的話(huà),你找幾個(gè)大師級的人物問(wèn)問(wèn),他們對哪一種思想完全理解并應用了?)完全理解并應用他,但新的思想又出來(lái)了(這種思想說(shuō)的是軟件類(lèi)的思想,編程的思想,當然沒(méi)有孔子孟子之類(lèi)說(shuō))!好像說(shuō)了這么多,跟“萬(wàn)事萬(wàn)物皆有姻緣”沒(méi)有任何關(guān)系一樣,有,當然有,雖然這么多的思想沒(méi)有任何一個(gè)人完全貫通使用,但提出一種新的思想來(lái)使用,這是事物發(fā)展的需要,也是必然的趨勢,藝術(shù)性的編程不能實(shí)用于大型應用,因此提出工程化,面向過(guò)程使軟件升級特別困難,因此懶惰的人們提出了面向對象的復用思想
,但面向對象的思想還是復雜,懶惰的人們就想要做更少的工作,因此提出了面向方面!“萬(wàn)事萬(wàn)物皆有姻緣”因為人們的懶惰,所以該放下的就放下了,重新拿起新的事物!那么為什么現在在編程界又提出了控制反轉(Ioc)呢?下面我們看一個(gè)經(jīng)常引用的例子:
public Class MyClass{
Logger _logger=Logger.getLogger(this.getClass());//實(shí)例化_logger
public void helloWorld(String){
_logger.DEBUG("Start print .....");
System.out.println("Hello! The world!");
_logger.DEBUG("End print.......");
}
}
現在想,如果我實(shí)例化_logger的方式變了,變成了:
Logger _logger=Logger.getLogger("My Class DEBUG");
你們想想是不是應該要改上面這個(gè)已經(jīng)實(shí)現了的CLASS呢?結果是肯定的,如果我有10個(gè)類(lèi),100個(gè),那是不是要改10次,100次,我們稱(chēng)這樣的軟件開(kāi)發(fā)為藕合性太強或依賴(lài)太深!
“萬(wàn)事萬(wàn)物皆有姻緣”,控制反轉(Ioc)就是為了解決這樣的問(wèn)題而提出的原則!
二、面向對象思想回顧
首先我們回顧面向對象思想的一些內容,筆者在《軟件工程之面向對象軟件工程基礎(軟件工程實(shí)踐之三)》一文中概術(shù)面向對象時(shí),面向對象=類(lèi)+對象+繼承+通信,面向對象就是這樣的一種思想。將組件分類(lèi),使用時(shí)再實(shí)例化,類(lèi)與類(lèi)之間可以繼承,他們通過(guò)通信聯(lián)系。在這里我們要重新到一個(gè)概念是服務(wù),在很多面向對象文章中都提到了服務(wù),那么什么是服務(wù)呢?即一個(gè)類(lèi)向另一個(gè)類(lèi)提供他想要的接口(或方法)。
如下例:
public ClassA{
/*
*下面是ClassA提供給其它類(lèi)使用的接口
*/
public void bussiness(){
System.out.println("execute bussiness");
}
}
public ClassB{
ClassA classA=new ClassA();//classA即將向ClassB提供服務(wù)
public executebussiness(){
classA.bussiness();//調用了ClassA的bussiness方法來(lái)得到服務(wù)
}
}
另兩個(gè)概念就是服務(wù)提供者與服務(wù)使用者,從上例可以看出,ClassA就是服務(wù)提供者-提供業(yè)務(wù)服務(wù)的一方 ,ClassB就服務(wù)使用者(也稱(chēng)消費者)-使用業(yè)務(wù)服務(wù)的一方,在他們之間采用的聯(lián)系的方式我們叫做通信。 從上面的例子可以看出,服務(wù)提供者提供服務(wù)給消費者是特別直接的。這樣有如下壞處:
如果服務(wù)提供者變了呢?這樣是不是需要改動(dòng)每個(gè)服務(wù)消費者? 這樣的一種依賴(lài)關(guān)系?我們用什么來(lái)解決呢?Ioc,控制反轉,它讓服務(wù)消費者不直接依賴(lài)于服務(wù)提供者!
三、什么是控制反轉(Ioc)?
一種讓服務(wù)消費者不直接依賴(lài)于服務(wù)提供者一 種組件設計方式,一種減少類(lèi)與類(lèi)之間依賴(lài)的設計原則,一種使服務(wù)與服務(wù)提供者完全分開(kāi)的設計原則。我們將第一個(gè)例子改進(jìn)一下:
public Class MyClass{
Logger _logger;
public void setLogger(Logger logger){
this._logger=logger;
}
public void helloWorld(){
_logger.DEBUG("Start DEBUG print....");
System.out.println(“hello world");
_logger.DEBUG("End DEBUG print.....");
}
}
現在調用時(shí):
public Class UseLogger{
Logger _logger=Logger.getLogger(this.getClass());
public void execute(){
MyClass myClass=new MyClass();
myClass.setLogger(_logger);
myClass.helloWorld();
}
}
這樣,我們發(fā)現,整個(gè)使用Logger的控制權全部在客戶(hù)端,即Logger給MyClass提供的服務(wù)的方式已經(jīng)提交給了最終客房,完全取決于客戶(hù)端的需要,而不是在MyClass調用服務(wù)時(shí)就依賴(lài)于Logger了,這樣,服務(wù)提供者(Logger)與消費者(MyClass)之間的依賴(lài)就少了很多。這就是Ioc的一種具體實(shí)現方式。
我們可以看出來(lái),Ioc更通俗的一種解釋就是:我在需要服務(wù)時(shí),才告訴你怎么去給我提供服務(wù)_logger的定義在MyClass中只是一個(gè)預留的定義,直到UseLogger最終消費時(shí)才將具體怎么使用Logger通知給MyClass.
四、控制反轉的幾種設計方式?
目前控制反轉的幾種實(shí)現方式:
.基于方法的(Method-based Ioc,Type-0)
.基于接口的(Interface-based Ioc,Type-1)
.基于設值的(Setter-based Ioc,Type-2)
.基于構造的(Construtor-based Ioc,Type-3)
.Type-0
基于方法的實(shí)現,主要依賴(lài)的是接口,,服務(wù)提供者將自己的接品傳遞給消費者方法,而不是將具體的實(shí)現類(lèi)型傳遞過(guò)去,如下:
public interface MyClass{
public void helloWorld(Logger _logger);
}
調用時(shí):
Logger _logger=Logger.getLogger(this.getClass());
MyClass myClass=new MyClassImpl();
myClass.helloWorld(_logger);
從上面可以看出,這樣Logger與MyClass的具體服務(wù)都依賴(lài)于接口MyClass,Logger提供服務(wù)時(shí)也止于方法上,Logger服務(wù)提供與
MyClassImpl消費之間隔開(kāi)了,
.Type-1
基于接口的實(shí)現,主要是采用容器來(lái)管理服務(wù)提供者,消費者只要實(shí)現服務(wù)提供者的接口就可以達到服務(wù)的目的了。
如下:
Public Class MyClassImpl implements MyClass,Logger {
protected Logger _logger;
/*
*實(shí)現Logger的enableLogger有效方法
*/
public void enableLogger(Logger logger){
_logger=logger;
}
public void helloWorld(){
_logger.DEBUG("ENABLE");
}
}
怎么調用 :
MyClass myClass=new MyClass();
Logger _logger=Logger.getLogger(this.getClass());
Container.enableLogger(myClass,_logger);
myClass.helloWorld();
可以看出,這樣的實(shí)現,必須要有珍上容器來(lái)檢查是否實(shí)現了Logger接品,如果實(shí)現兇才能有效,否則就沒(méi)有作用。這樣的方法大大的減少了類(lèi)與類(lèi)之間的依賴(lài).但必須要實(shí)現一個(gè)容器。而且發(fā)現,每一個(gè)實(shí)現的類(lèi)都直接跟服務(wù)提供者(本例就是Logger)的接口有了很直接的聯(lián)系。
.Type-2
的方式,就是通過(guò)BEAN的setter方法來(lái)給成員變量賦值,我們采用這種方式,就可以通過(guò)一個(gè)配置文件,配置好相應的Bean,然后通過(guò)一個(gè)容器來(lái)得到這個(gè)服務(wù)Bean,這樣組件之間的依賴(lài)就已經(jīng)少了。
如:
public Class MyClass{
private Logger logger;
public void setLogger(Logger logger){
this.logger=logger;
}
public void helloWorld(){
logger.DEBUG("ENABLE");
}
}
如Spring采用的方式就是如此,它的全過(guò)程如下:
配置文件:
<Beans>
<Bean id="logger" Class="Logger"/>
<Bean id="myClass" Class=MyClass">
<property name="logger"><ref bean="logger"/></property>
</Bean>
</Beans>
調用:
InputStream is=Client.class.getClassLoader().getResourceAsStream("bean.xml");
BeanFactory beans=new XMLBeanFacotory(is);
MyClass myClass=(MyClass)beans.getBean("myClass");
myClass.helloWorld();
從上面可以看出,所有的服務(wù)提供類(lèi)及消費類(lèi)都已經(jīng)通過(guò)一個(gè)容器類(lèi)來(lái)提取配置文件統一管理了。
.Type-3
基于構造的Ioc,顧名思義,就是通過(guò)構造子來(lái)傳遞服務(wù)提供者的具體實(shí)例來(lái)實(shí)例化服務(wù)接口。
如下:
public Class MyClass{
private Logger _logger;
public MyClass(Logger logger){
_logger=logger;
}
public void helloWorld(){
_logger.DEBUG("ENABLE");
}
}
這樣,注冊服務(wù)組件時(shí)在構造方法處完成,但,這樣一來(lái)繼承和構造時(shí)都有一定的困難。
五、結語(yǔ)
Ioc的一個(gè)真正的目的就是移植,減少組件的依賴(lài)性!但我們通過(guò)上面的例子,有沒(méi)有發(fā)現,這中間的依賴(lài)還很大呢?比如:如果我要將Logger類(lèi)移植使用另外一個(gè)公司的產(chǎn)品的時(shí)候,這個(gè)時(shí)候,要移植的量是不是還很大呢?
六、附錄
1、術(shù)語(yǔ)
控制反轉(Ioc-Inversion Of Control);
容器(container),用于組件創(chuàng )建,生存的環(huán)境;
框架(framework::一組用于對特定領(lǐng)域構造的可復用的基礎組件
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=433422
聯(lián)系客服