構(gòu)件復(fù)用作為一種現(xiàn)實有效的軟件開發(fā)方法正受到越來越多的重視和研究。近年來,隨著DNA,中間件等三層體系結(jié)構(gòu)和分布式對象的興起,為基于可復(fù)用構(gòu)件的軟件體系結(jié)構(gòu)賦予了更大的內(nèi)涵。從而使我們在評價構(gòu)件復(fù)用的意義時,不應(yīng)只局限于開發(fā)周期的縮短,軟件質(zhì)量的可靠提高,更要認(rèn)識到構(gòu)件組裝式的開發(fā)在系統(tǒng)維護(如版本更新,功能增刪等),分布式計算等方面所帶來的極大便利。由于維護階段占軟件開發(fā)周期總成本的67%左右,因此構(gòu)件重用對整個軟件產(chǎn)業(yè)的重要意義就不言而喻了.從維護和分布計算的角度考慮,構(gòu)件復(fù)用就是要實現(xiàn)"即插即用".
傳統(tǒng)的面向?qū)ο缶幊陶Z言中,基于繼承機制的類的復(fù)用,只是源代碼級的重用,在源代碼不可得的情況下(構(gòu)件產(chǎn)業(yè)化的發(fā)展趨勢下,這種情況很普遍),就變得毫無意義了.更為重要的是,聯(lián)編以后,類構(gòu)件就只是一個邏輯上的虛幻的概念了,不會給將來可能的處理帶來任何方便.
MicroSoft自90年代初就進行了COM的開發(fā),歷經(jīng)OLE1,VBX組件,OLE2,ActiveX,COM+的不斷完善,現(xiàn)已成為一個相當(dāng)成熟的組件模型,對構(gòu)件復(fù)用提供了有力的底層支持.
一 COM對象的封裝
COM是一個二進制的標(biāo)準(zhǔn),它詳細(xì)規(guī)定了一個COM組件所應(yīng)具有的內(nèi)存結(jié)構(gòu).COM對象間的交互完全基于對此內(nèi)存結(jié)構(gòu)的操作.因此可以在很大程度上忽略不同編程語言,應(yīng)用環(huán)境之間的差別,解決了重新編譯重新發(fā)行的問題.二進制代碼級的兼容性要求操作系統(tǒng)的支持,但是COM描述對象連接的方法與傳統(tǒng)的API式共享系統(tǒng)服務(wù)不同.連接建立后,COM底層庫不再被需要,停止耗用系統(tǒng)資源,與API相比,操作系統(tǒng)必須一直管理組件之間的連接.
COM用接口的概念對組件的功能屬性進行完全的封裝.與組件的通信必須通過接口進行.接口不僅是一個邏輯上的概念,而且也存在著與之相對應(yīng)的物理內(nèi)存結(jié)構(gòu)(VTABLE).一個對象可以對應(yīng)多個接口,一個接口也可以由多個對象所實現(xiàn),表現(xiàn)出靈活的多態(tài)性.同時也為版本管理提供了方便.當(dāng)使用新版本的組件替換老版本時,只要該組件實現(xiàn)了舊版本的接口(通過包容,聚合等手段),就保證了其與原用軟件系統(tǒng)的兼容.同時新增功能(新的接口)又可被自然的使用.
接口完全封裝了內(nèi)部功能,屬性的具體實現(xiàn),使得COM對象對外表現(xiàn)為"黑盒"結(jié)構(gòu),完全吻合面向?qū)ο笙到y(tǒng)所要求的"強內(nèi)聚性".但由于對接口的過多強調(diào),COM組件一般不具備廣泛提倡的"弱耦合性"的特點.不過,微軟一向重視接口的不變性,試圖用接口的標(biāo)準(zhǔn)化推動服務(wù)的標(biāo)準(zhǔn)化,以接口為基礎(chǔ)為軟件復(fù)用建立實用的框架.其實ActiveX技術(shù)規(guī)范中的相當(dāng)大一部分都是通過定義標(biāo)準(zhǔn)的接口及其相互之間的邏輯關(guān)系來確定的.
二 自動化
在考慮調(diào)用接口內(nèi)成員函數(shù)的具體實現(xiàn)時,就會發(fā)現(xiàn)由于組件的特殊性,這種實現(xiàn)需要與通常完全不同的規(guī)范. 需要解決的問題有:源程序中如何標(biāo)識一個組件(物理上,就是一段已經(jīng)存在了的,具有一定功能的二進制代碼),對于組件內(nèi)特定函數(shù)的調(diào)用,編譯器將如何做出處理,如何進行參數(shù)的檢驗及返回值的收集.
在舊的編程模式中,以上問題的解決均需要一個對組件進行充分定義的說明性文件.而且,該說明性文件的格式必須完全符合所用的編程語言的語法.這就產(chǎn)生了以下一些矛盾.首先,為每一個發(fā)行組件均配置各種不同版本的說明文件在實踐中并不可行;第二,即使這樣的頭文件通過類型庫自動轉(zhuǎn)換得到,為各種編程語言提供這種轉(zhuǎn)換工具同樣是不可行的;第三,組件中所用到的數(shù)據(jù)類型并不一定總能與目標(biāo)編稱語言一一對應(yīng);第四,這種笨拙的實現(xiàn)方法,與構(gòu)件的"即插即用"概念相去甚遠(yuǎn),程序員難以接受.
針對以上問題,COM規(guī)范提出了自動化技術(shù),較好的實現(xiàn)了以符號為導(dǎo)航的動態(tài)綁定. Idispatch是實現(xiàn)這一點的關(guān)鍵接口.
Class Idispatch : public Iunknown
{
public:
virtual HRESULT GetTypeInfoCount(UINT * pctinfo) = 0;
virtual HRESULT GetTypeInfo(UINT iTinfo,LCID
lcid,ItypeInfo ** ppTInfo) = 0;
virtual HRESULT GetIDsOfNames(REFIID riid,LPOLESTR *
rgszNames,UINT cNames,LCID lcid,DISPID *rgDispId) = 0;
virtual HRESULT Invoke(DISPID dispIdMember,REFIID
riid,LCID lcid,WORD wFlags,DISPPARAMS *pDispParams,
VARIANT *pVarResult,EXCEINFO *pExcepInfo,
UINT *puArgErr) = 0;
};
在自動化對象中,每一個成員函數(shù)均對應(yīng)一個分發(fā)ID(DISPID),服務(wù)導(dǎo)航器InVoke就是通過此分發(fā)ID建立與目標(biāo)函數(shù)的連接.可以用GetIDsofName實現(xiàn)符號(函數(shù)名)到ID的映射.因此,應(yīng)用程序就有了一個統(tǒng)一的界面來使用組件提供的各項服務(wù).
對于數(shù)據(jù)類型的兼容性問題,微軟提供了一個生硬的解決方法.定義一個盡可能"包羅萬象"的大的數(shù)據(jù)結(jié)構(gòu)VARIANT
typedef struct tagVARIANT
{
VARTYPE vt; //類型標(biāo)示
union
{ // 傳值類型
short iVal;
long lVal;
byte bVal;
float fltVal;
……
// 引用類型
short *piVal;
……
IUnknown **ppunkVal;
IDispatch **ppdispVal;
VARIANT *pvarVal;
void *byref;
};
};
傳統(tǒng)的面向?qū)ο缶幊陶Z言中,基于繼承機制的類的復(fù)用,只是源代碼級的重用,在源代碼不可得的情況下(構(gòu)件產(chǎn)業(yè)化的發(fā)展趨勢下,這種情況很普遍),就變得毫無意義了.更為重要的是,聯(lián)編以后,類構(gòu)件就只是一個邏輯上的虛幻的概念了,不會給將來可能的處理帶來任何方便.
MicroSoft自90年代初就進行了COM的開發(fā),歷經(jīng)OLE1,VBX組件,OLE2,ActiveX,COM+的不斷完善,現(xiàn)已成為一個相當(dāng)成熟的組件模型,對構(gòu)件復(fù)用提供了有力的底層支持.
一 COM對象的封裝
COM是一個二進制的標(biāo)準(zhǔn),它詳細(xì)規(guī)定了一個COM組件所應(yīng)具有的內(nèi)存結(jié)構(gòu).COM對象間的交互完全基于對此內(nèi)存結(jié)構(gòu)的操作.因此可以在很大程度上忽略不同編程語言,應(yīng)用環(huán)境之間的差別,解決了重新編譯重新發(fā)行的問題.二進制代碼級的兼容性要求操作系統(tǒng)的支持,但是COM描述對象連接的方法與傳統(tǒng)的API式共享系統(tǒng)服務(wù)不同.連接建立后,COM底層庫不再被需要,停止耗用系統(tǒng)資源,與API相比,操作系統(tǒng)必須一直管理組件之間的連接.
COM用接口的概念對組件的功能屬性進行完全的封裝.與組件的通信必須通過接口進行.接口不僅是一個邏輯上的概念,而且也存在著與之相對應(yīng)的物理內(nèi)存結(jié)構(gòu)(VTABLE).一個對象可以對應(yīng)多個接口,一個接口也可以由多個對象所實現(xiàn),表現(xiàn)出靈活的多態(tài)性.同時也為版本管理提供了方便.當(dāng)使用新版本的組件替換老版本時,只要該組件實現(xiàn)了舊版本的接口(通過包容,聚合等手段),就保證了其與原用軟件系統(tǒng)的兼容.同時新增功能(新的接口)又可被自然的使用.
接口完全封裝了內(nèi)部功能,屬性的具體實現(xiàn),使得COM對象對外表現(xiàn)為"黑盒"結(jié)構(gòu),完全吻合面向?qū)ο笙到y(tǒng)所要求的"強內(nèi)聚性".但由于對接口的過多強調(diào),COM組件一般不具備廣泛提倡的"弱耦合性"的特點.不過,微軟一向重視接口的不變性,試圖用接口的標(biāo)準(zhǔn)化推動服務(wù)的標(biāo)準(zhǔn)化,以接口為基礎(chǔ)為軟件復(fù)用建立實用的框架.其實ActiveX技術(shù)規(guī)范中的相當(dāng)大一部分都是通過定義標(biāo)準(zhǔn)的接口及其相互之間的邏輯關(guān)系來確定的.
二 自動化
在考慮調(diào)用接口內(nèi)成員函數(shù)的具體實現(xiàn)時,就會發(fā)現(xiàn)由于組件的特殊性,這種實現(xiàn)需要與通常完全不同的規(guī)范. 需要解決的問題有:源程序中如何標(biāo)識一個組件(物理上,就是一段已經(jīng)存在了的,具有一定功能的二進制代碼),對于組件內(nèi)特定函數(shù)的調(diào)用,編譯器將如何做出處理,如何進行參數(shù)的檢驗及返回值的收集.
在舊的編程模式中,以上問題的解決均需要一個對組件進行充分定義的說明性文件.而且,該說明性文件的格式必須完全符合所用的編程語言的語法.這就產(chǎn)生了以下一些矛盾.首先,為每一個發(fā)行組件均配置各種不同版本的說明文件在實踐中并不可行;第二,即使這樣的頭文件通過類型庫自動轉(zhuǎn)換得到,為各種編程語言提供這種轉(zhuǎn)換工具同樣是不可行的;第三,組件中所用到的數(shù)據(jù)類型并不一定總能與目標(biāo)編稱語言一一對應(yīng);第四,這種笨拙的實現(xiàn)方法,與構(gòu)件的"即插即用"概念相去甚遠(yuǎn),程序員難以接受.
針對以上問題,COM規(guī)范提出了自動化技術(shù),較好的實現(xiàn)了以符號為導(dǎo)航的動態(tài)綁定. Idispatch是實現(xiàn)這一點的關(guān)鍵接口.
Class Idispatch : public Iunknown
{
public:
virtual HRESULT GetTypeInfoCount(UINT * pctinfo) = 0;
virtual HRESULT GetTypeInfo(UINT iTinfo,LCID
lcid,ItypeInfo ** ppTInfo) = 0;
virtual HRESULT GetIDsOfNames(REFIID riid,LPOLESTR *
rgszNames,UINT cNames,LCID lcid,DISPID *rgDispId) = 0;
virtual HRESULT Invoke(DISPID dispIdMember,REFIID
riid,LCID lcid,WORD wFlags,DISPPARAMS *pDispParams,
VARIANT *pVarResult,EXCEINFO *pExcepInfo,
UINT *puArgErr) = 0;
};
在自動化對象中,每一個成員函數(shù)均對應(yīng)一個分發(fā)ID(DISPID),服務(wù)導(dǎo)航器InVoke就是通過此分發(fā)ID建立與目標(biāo)函數(shù)的連接.可以用GetIDsofName實現(xiàn)符號(函數(shù)名)到ID的映射.因此,應(yīng)用程序就有了一個統(tǒng)一的界面來使用組件提供的各項服務(wù).
對于數(shù)據(jù)類型的兼容性問題,微軟提供了一個生硬的解決方法.定義一個盡可能"包羅萬象"的大的數(shù)據(jù)結(jié)構(gòu)VARIANT
typedef struct tagVARIANT
{
VARTYPE vt; //類型標(biāo)示
union
{ // 傳值類型
short iVal;
long lVal;
byte bVal;
float fltVal;
……
// 引用類型
short *piVal;
……
IUnknown **ppunkVal;
IDispatch **ppdispVal;
VARIANT *pvarVal;
void *byref;
};
};