在C++中使用IoC及DSM框架
自Web和Java橫空出世以來,軟件開發(fā)技術(shù),概念以及架構(gòu)的演變翻新有點讓人眼花繚亂,甚至應(yīng)接不暇。然而,恪守技術(shù)
含量至上遺風(fēng)的C++鐵桿精英們似乎是其中的另類,與標(biāo)新立異擯棄繁文褥節(jié)的Java,Ruby新生代之間儼如隔世。不用說DSM,就連IoC這個起源于
C++年代甚至C++土壤的概念和方法雖然在墻外其他晚輩語言部落中廣受青睞,在C++紅墻內(nèi)卻反而遭長期冷落。IoC和DSM技術(shù)雖然看似簡單無比平淡
無奇遍地都是,甚至被很多C++大老們嗤之以鼻(甚至斥責(zé)為狗皮膏藥),但卻能大大提升C++軟件開發(fā)的效率及質(zhì)量,而且能徹底簡化和凈化很多繁瑣丑陋甚至危險的C++原始解決
方案。比如,由IBM領(lǐng)頭忽悠的一個所謂“服務(wù)組件架構(gòu)”SCA(Service Component
Architecture)的廠商標(biāo)準(zhǔn),其C++組件容器的參考實現(xiàn)(RI)雖由IBM幾個老大全時操刀,歷時兩年卻仍然困難重重地蹣跚在Apache孵
化項目階段。不僅如此,這個沿用傳統(tǒng)思維和方法的參考實現(xiàn)使用起來也相當(dāng)繁瑣,甚至危險(用戶將被迫采用無類型驗證系統(tǒng)的C-style
cast),并且有很多極不自然甚至丑陋的限制(比如對線程模型的特殊要求)(參見 SCA considered harmful
一文)。與之對比,如果采用IoC和DSM技術(shù)去實現(xiàn)同樣的SCA
C++組件容器,一個初級菜鳥程序員卻能在幾天甚至幾小時內(nèi)以區(qū)區(qū)數(shù)百行淺顯易懂的代碼輕而易舉地大功告成,且其結(jié)果還遠(yuǎn)勝于IBM老大們嘔心瀝血打造的
參考版(易用,類型安全,清除所有不合理限制)(參見 SCA as a DSM 一文)。此類事半功倍的例子在使用IoC和DSM框架的開發(fā)中屢見不鮮。其開發(fā)效率一個數(shù)量級以上的跳躍改進(jìn)也絕非天方夜譚。70年代關(guān)系數(shù)據(jù)庫及SQL技術(shù)的引入就使數(shù)據(jù)庫應(yīng)用的開發(fā)效率提高了近兩個數(shù)量級。
1. IoC
IoC字面上的意思是“控制反轉(zhuǎn)”(Inversion of Control)。然而其具體含義五花八門的說法卻很容易讓人一頭霧水。這些說法往往是過多地關(guān)注IoC表面的甚至是字面的含義,卻忽略了IoC被用來解決的實質(zhì)問題(也無視了這個概念的歷史和使用現(xiàn)狀)。Martin Fowler 就把IoC闡述甚至更名為“依賴注入”(Dependency Injection )設(shè)計模式。而IoC鼻祖之一 Stefano Mazzocchi 卻指出Martin Fowler這個忽悠了一大批人的說法實際上是不得要領(lǐng)(Martin Fowler文章中???碼例子確實非常誤導(dǎo))。所以,這里并不急于給IoC下一個教條定義,而是從IoC的實質(zhì)目的開始探討。
IoC 的概念是Michael Mattson在1996年一篇討論面向?qū)ο罂蚣?Object Oriented Frameworks)的文章中提出的。面向?qū)ο笤O(shè)計及編程(OOD/OOP)的基本思想簡單地說就是把復(fù)雜軟件系統(tǒng)分解成通過接口相互合作的對象。這些 對象類的內(nèi)部實現(xiàn)之間并不互相牽扯,因而降低了問題的復(fù)雜性,且可獨立靈活地被重用和擴展。自然而然,經(jīng)典面向?qū)ο蟮木幊陶Z言(如C++,Java)的側(cè) 重點就是提供語言機制來方便并簡化這種基于對象類的分解,重用和擴展。然而,一個軟件系統(tǒng)的開發(fā)效率,可擴展性,以及部署維護(hù)升級的靈活性等并不完全由其 模塊化分解的程度和抽象的優(yōu)劣所決定。很大程度上,將這些分散部件有效地組裝成一個緊密合作的整體更 是決定該軟件項目成???及其產(chǎn)品系統(tǒng)優(yōu)劣的關(guān)鍵。
以 支持對象分解為己任的經(jīng)典面向?qū)ο笳Z言(如C++,Java)并沒有引入超越傳統(tǒng)命令式語言(imperative language)以外的系統(tǒng)組裝部署和配置手段(當(dāng)然,Java 5,C#現(xiàn)在都開始往這方面添料)。因而,雖然它們能夠有效地應(yīng)付底層子系統(tǒng)的拼裝和連接,但在進(jìn)行大范圍基于組件(既高層業(yè)務(wù)層模塊)層次的相應(yīng)作業(yè)時 就顯得簡陋,死板,冗長和低效。比如,在使用各種std的IO流類,STL容器類以及boost庫類這些底層模塊類時,采用語言本身的機制(自動變量或用 new算符)直截了當(dāng)?shù)貙嵗@些被使用類的對象就盡善盡美了。但在使用高層業(yè)務(wù)模塊時,為了避免對其具體實現(xiàn)類的依賴,人們不得不疊床架屋對語言機制進(jìn) 行額外的手工包裝。應(yīng)運而生的是一系列處理所謂管線邏輯(plumbing logic,既非業(yè)務(wù)邏輯)的設(shè)計模式,比如factory,builder,directory,adaptor,singleton, configuration/property manager,factory的manager甚至manager的manager等等。遺憾的是,一個世紀(jì)以來這些實際上是彌補語言缺陷的權(quán)宜之計反 而受到狂熱追捧而非深入反思。另外,在傳統(tǒng)軟件的設(shè)計和實現(xiàn)中,業(yè)務(wù)邏輯往往直接調(diào)用這些管線邏輯,從而破壞了業(yè)務(wù)邏輯的簡潔性和獨立性(比如增加了單元 測試的困難)。更重要的是,軟件組裝部署和配置的邏輯是支離破碎地散落混跡于各個業(yè)務(wù)邏輯組件中,既不直觀(往往是見樹不見林)也不靈活(牽一發(fā)則可能動 全身)。往往使得在宏觀結(jié)構(gòu)上理解,維護(hù),修改和擴充一個現(xiàn)有軟件要反而難于當(dāng)初從頭開發(fā)這個軟件。
為 解決以上問題,Michael Mattson提出了面向?qū)ο罂蚣艿腎oC設(shè)計原則。依照該原則,管線邏輯被轉(zhuǎn)移并集中至軟件框架內(nèi),業(yè)務(wù)邏輯模塊并不需知道更不必調(diào)用組件框架的服務(wù), 例如不用關(guān)心和調(diào)用其factory或lookup其directory或context等。軟件的組裝部署和配置完全是由管線邏輯框架反過來主動控制業(yè) 務(wù)邏輯模塊來安排。Michael Mattson用所謂的好萊塢原則(Hollywood Principle)“別來電(問)我,我會去電(告訴)你”(don't call me, I will call you)形象地比喻了這一設(shè)計思想。這個比喻中的“我”指的是負(fù)責(zé)管線邏輯的組件框架,“你”則是被其調(diào)遣配置的一個組件。比如,在一個業(yè)務(wù)邏輯模塊A需 要調(diào)用另一個業(yè)務(wù)邏輯模塊B的場景中,傳統(tǒng)的非IoC的設(shè)計(比如EJB2.0)是讓A調(diào)用管線邏輯(B的factory或某個directory服務(wù)) 來獲得B的引用(或指針)。在IoC框架內(nèi),框架不但完成A和B的實例化并保持追蹤,而且B的實例引用(或指針)也是由框架主動調(diào)用A的接口函數(shù)(比如構(gòu) 造及賦值函數(shù)等)賦予 A。從而,A的實現(xiàn)可以專著于業(yè)務(wù)邏輯,而管線細(xì)節(jié)(比如B的實例化及如何獲得其引用或指針)可以讓外部框架透明地安排妥當(dāng)。這種架構(gòu)完全避免了業(yè)務(wù)邏輯 對具體管線邏輯框架的牽連從而降低了業(yè)務(wù)邏輯模塊的復(fù)雜度,但更重要的是集中的組裝部署和配置邏輯為提高軟件宏觀結(jié)構(gòu)的直觀性和靈活性鋪平了道路(后面還 將具體討論)。
簡短概 括一下,從概念上說,IoC就是模塊化軟件組裝部署配置框架的一個設(shè)計原則。依該原則,業(yè)務(wù)邏輯模塊既不需要處理管線邏輯也可以對外部管線框架本身一無所 知(agnostic)。軟件的搭接完全由外部管線框架對業(yè)務(wù)邏輯模塊的主動操控來完成。從具體實現(xiàn)上說,除了一些開發(fā)工具以外,IoC框架不過是一個封 裝了必備的管線邏輯及IoC機制的輕型類庫(比如PocoCapsule/C++ IoC類 庫大約是70K)。從使用上說,用戶制作好業(yè)務(wù)邏輯組件(見下面非侵入性與POCO討論),并將軟件組裝及部署描述(見后面討論)提供給IoC框架(作為 IoC框架庫函數(shù)的???用參量,或直接驅(qū)動一系列庫函數(shù)調(diào)用)。IoC容器(被調(diào)用的庫函數(shù))將參照用戶的描述相應(yīng)地實例化和配置各個組件并將它們搭接 為所希望的部署狀態(tài)。
2. 非侵入性與POCO
對 于象C++和Java這類不支持動態(tài)類型的語言環(huán)境,一個很自然的問題就是業(yè)務(wù)邏輯組件需要支持什么樣的公共接口以使外部IoC框架能對其進(jìn)行操控。早期 的組件框架(比如Apache Avalon,EJB2.0,CORBA組件模型 CCM,JTRS-SCA等)幾乎清一色地采用侵入式(invasive)設(shè)計,也就是強制規(guī)定業(yè)務(wù)邏輯模塊(稱作bean)必須與特定的公共組件接口模 型兼容,既支持由組件框架定義的用來對組件進(jìn)行部署配置的公共接口類型或基類(base class)以及進(jìn)行實例化的所謂home接口。侵入式設(shè)計不僅學(xué)習(xí)和使用繁瑣(EJB2.0和CCM均是追求繁瑣,以繁瑣冒充強大的惡例),也大大地限制了組建框架的 開放性和適應(yīng)性。因為眾口難調(diào),所以幾乎每一個問題領(lǐng)域均定義了N個自己的組件框架和組件接口模型(比如機器人領(lǐng)域里就至少有10個)。侵入式設(shè)計導(dǎo)致組 件接口模型只能被其特定的框架所支持,從而形成了各自為政老死不相往來的組件框架孤島,限制了組件的重用范圍以及框架的通用性。大量這類侵入式組件框架以 及與之相應(yīng)的上下左右整個配套開發(fā)體系(如果僥幸有的話)均僅僅是在小范圍內(nèi)被采用,并以高成本在低水平上無謂地被重復(fù)開發(fā)和維護(hù)(CCM就是這方面最惡 名昭著的例子)。
因 此,現(xiàn)代IoC框架大都采用非侵入式(non-invasive)設(shè)計,也就是不對組件接口模型(即接口及函數(shù)簽名)做任何規(guī)定。換句話說,非侵入的 IoC框架一視同仁地支持任何組件接口模型,包括已經(jīng)被定義的和還未被定義的模型,也包括標(biāo)準(zhǔn)組織定義的或用戶自定義的模型。這些組件接口模型可以采用 (或不采用)任何公共或自定義接口,模板(template)或基類(也可以根本不是C++對象,比如C/C++函數(shù)),可以采用任何(合理的)實例化或 回收手段和部署配置函數(shù),包括構(gòu)造或析構(gòu)函數(shù),各種自定義 factory/pool/directory lookup函數(shù),以及各種全局或成員函數(shù)等(參見開源C++非侵入式IoC框架項目 PocoCapsule/C++ IoC的介紹 )。在非侵入的C++ IoC組件框架中,因為所有組件無論其接口模型的新舊美丑高矮胖瘦均被一視同仁地按平頭百姓對待,故均被統(tǒng)稱為“平庸C++對象”(Plain Old C++ Object )或POCO(相應(yīng)于Java中的“平庸Java對象”POJO)。
3. 軟件組裝及部署描述
在 使用IoC框架的C++軟件開發(fā)過程中,管線模塊及管線銜接代碼轉(zhuǎn)移并集中至IoC框架內(nèi),業(yè)務(wù)邏輯組件(高層模塊或庫類)的開發(fā)只需關(guān)注業(yè)務(wù)邏輯本身。 軟件的搭接由IoC容器(或庫類)按照用戶提供的軟件組裝及部署描述(assembly and deployment description)完成。因為由分散組件搭接成的軟件呈樹狀或更廣義的圖狀結(jié)構(gòu),所以,軟件組裝及部署描述就是對這種樹圖結(jié)構(gòu)模型的描述。這種描述 通常有三種表達(dá)(編寫)形式,既指令代碼(code),元數(shù)據(jù)(metadata),和用戶數(shù)據(jù)(data)。
雖 然經(jīng)典面向?qū)ο笳Z言(比如C++和Java)可以有效地按程序步驟構(gòu)造樹圖狀數(shù)據(jù)結(jié)構(gòu),但對結(jié)構(gòu)整體的“模型描述”卻蒼白無力。在C++中,要搭建一個樹 圖結(jié)構(gòu)的代碼不外乎就是一步步地去調(diào)用類似allocNode(),addNode(),wireNodes()等等函數(shù)。樹圖結(jié)構(gòu)模型在這種指令式的 (imperative)“步驟描述”中蕩然無存。對于管線邏輯比較簡單的應(yīng)用(例如底層或子模塊),結(jié)構(gòu)模型并不至關(guān)重要甚至是多此一舉,而步驟描述或 干脆拋棄IoC框架也許更直接了當(dāng)。然而對于管線邏輯較復(fù)雜的應(yīng)用,對結(jié)構(gòu)搭接具體步驟描述所引入的復(fù)雜性則與使用IoC框架抽象???線邏輯的原始初衷背道 而馳。
所謂元數(shù)據(jù)表述 就是以編程語言的元數(shù)據(jù)結(jié)構(gòu)來描述軟件的管線結(jié)構(gòu)模型。其實質(zhì)就是利用傳統(tǒng)指令性編程語言中類(class)結(jié)構(gòu)定義的聲明式語法來表達(dá)管線結(jié)構(gòu)。比如, 如果一個結(jié)構(gòu)有10個節(jié)點(組件),按此方法就是讓用戶定義一個包含10個相應(yīng)成員函數(shù)的類(class)并輔助以相應(yīng)注釋(annotation)標(biāo)識 管線連接。進(jìn)行軟件搭接時,IoC框架通過反射機制來解讀這個類的結(jié)構(gòu)并將其看做組件搭接的管線結(jié)構(gòu)描述。這種方法,看似提供了一種聲明式的 (declarative)模型描述,實際上則屬于一種牽強附會甚至是生搬硬套的kludge,除了能滿足“只使用編程語言本身來表述管線邏輯”這一教義 心態(tài)之外不具任何正面意義。
IoC 框架中有效靈活自然直觀的結(jié)構(gòu)描述形式恰恰就是被大牛們鄙視為惡俗的用戶文本數(shù)據(jù)描述形式。無論大牛們對這種C++和Java語法機制以外的方法如何深惡 痛絕,都不得不面對下面一個尷尬的窘境。C++和Java這類被他們(比如紅帽Jboss的首席科學(xué)家)奉為萬能銀彈的編程語言中并不提供對樹圖結(jié)構(gòu)整體 具體實例的有效描述手段。因為這類語言的目的僅僅是提供對象類的包裝抽象機制,而并不是提供具體多對象系統(tǒng)整體部署結(jié)構(gòu)的模型表述方法(更不要說對各種模 型之間變換,甚至變換的變換的聲明式描述)。
4. 基于XML的組裝及部署描述
主 流IoC框架(甚至很多傳統(tǒng)非IoC組件框架如EJB2.0和CCM)中用戶文本數(shù)據(jù)形式的組裝及部署描述大都是基于XML。XML的設(shè)計目的恰恰就是提 供對樹圖結(jié)構(gòu)的聲明式描述。對用戶來說,XML標(biāo)準(zhǔn)及技術(shù)成熟穩(wěn)定,已被普遍采用和支持(各種XML解析器和工具滿天飛)。另外,XML框架內(nèi)具備完善的 聲明式結(jié)構(gòu)轉(zhuǎn)換技術(shù)(XSLT,XQuery),為從底層通用IoC組件部署描述提高到“針對問題域的特定建模”DSM(domain specific modeling)準(zhǔn)備???了理想平臺。最重要的是,與SQL類似,XML是種???編程菜鳥都大呼容易的簡單直觀技術(shù),這就使得很多領(lǐng)域?qū)<夷軌驅(qū)M件構(gòu)成的 系統(tǒng)進(jìn)行搭建和部署。
下面以開源項目PocoCapsule/C++ IoC框架 中的一個具體例子來介紹這個方法。這個例子的完整代碼以及文檔在PocoCapsule源代碼包 和安裝包 中均可找到,也可以在http://www.pocomatic.com/docs/cpp-examples/basic-ioc/gps 上在線瀏覽。
這個例子中所要搭建部署的是一個如下圖所示包含定時觸發(fā)器(tick generator),GPS定位器(gps locator),導(dǎo)航???示器(navigate display)三個組件的GPS系統(tǒng)。
PocoCapsule容器支持所謂POCO(既前面所說的“平庸C++對象”),從而對組件接口模型幾乎沒有任何限制。這三個組件的基類TickGen,GPSLocator和NavDisplay都是由用戶自己在Interfaces.h 中如下定義的:
這些組件的具體實現(xiàn)類TickGenImpl,GPSLocatorImpl和NavDisplayImpl則大致定義如下(忽略所有與組裝不相關(guān)細(xì)節(jié))。
這 些組件基類和具體實現(xiàn)類的定義以及它們的編譯連接(動態(tài)或靜態(tài)庫均可)等開發(fā)制作方式與一般C++應(yīng)用模塊沒任何區(qū)別,完全不需考慮IoC容器。它們甚至 可以是IoC時代以前已由第三方制作好,不提供源代碼的模塊。在這個例子中,除了需要用到這些組件實現(xiàn)類的構(gòu)造算子原型以外省略掉了其他所有不需要關(guān)心的 實現(xiàn)細(xì)節(jié)。
接下來,可以用PocoCapsule的XML語法來描述這個GPS應(yīng)用。PocoCapsule采用與Spring Framework盡量相近的XML文本格式(schema,定義在poco-application.context.dtd 中),并針對C++語言特征進(jìn)行了擴充。比如,將bean實例化后的setter調(diào)用普遍化為任何IoC調(diào)用。在這個文本格式中, 一個POCO組件的實例將被聲明為一個<bean>元素,包含其構(gòu)造算子的參數(shù)(<method-arg>),實例化后的IoC (<ioc>),以及IoC方法的參量(也是<method-arg>)等子孫元素。比如,這個例子中的GPS應(yīng)用結(jié)構(gòu)就可以由 下面一段XML聲明(見setup.xml ):
這段XML聲明簡單直觀地表達(dá)了三個組件實例以及它們之間的互相銜接結(jié)構(gòu)。可以形象化地將它用相應(yīng)的C++形式表示如下:
雖 然這兩種描述看似表達(dá)同樣的概念,但實際上他們有本質(zhì)區(qū)別。C++版本表達(dá)的是構(gòu)造這個應(yīng)用的具體先后步驟,因此實際上必須重新修改上面C++代碼行的次 序才能讓程序正常編譯和工作。而XML版本表達(dá)的則是一種結(jié)構(gòu),而并非構(gòu)造這個結(jié)構(gòu)的步驟。結(jié)構(gòu)中各<bean>節(jié)點的實例化次序與它們在 XML表述中的先后次序無關(guān),而是由IoC容器根據(jù)用戶聲明的節(jié)點屬性(比如lazy-init的值)以及銜接時依賴關(guān)系的先后來決定。
至此,用戶僅需要將這個XML描述以文件或字符串形式交給PocoCapsule/C++ IoC容器(既以文件名或XML字符串為參量調(diào)用PocoCapsule/C++ IoC庫函數(shù),見main.C ),讓其自動組裝部署所描述???應(yīng)用。關(guān)于PocoCapsule詳細(xì)的使用及工作機制描述可參閱其入門教材 ,用戶手冊 ,及代碼實例 。
5. DSM和模型變換
前 面一節(jié)例子中用到的XML文本格式是由PocoCapsule容器支持的核心格式(core schema)。因為XML在IoC框架中用于描述基于組件應(yīng)用的結(jié)構(gòu)模型,所以這個格式也稱為建模格式。該建模格式與其他IoC容器所采用的XML格式 大同小異。這種格式有直觀易學(xué),格式定義緊湊,普遍適用等優(yōu)點。然而,這些這些具有雙刃劍特征的優(yōu)點也意味著相應(yīng)的缺點,諸如直接引用組件編程接口函數(shù)簽 名,表述力和抽象度低,容易造成冗長的聲明及低級錯誤。比如,前面GPS例子中,XML模型描述直接涉及了有兩個參量的TickGenImpl的構(gòu)造算 子。從該模型描述中,人們無從判斷這兩個參量的目的和意義。如果該模型描述聲明了不匹配的參量類型,用戶可以僥幸地得到IoC容器異常報告(雖然可能難于 解讀)。但如果這兩個參量類型聲明無誤,而它們的數(shù)值在無意間被相互顛倒了(因為恰好是同一類型),那么用戶就只能聽天由命了。以setter函數(shù)取代多 參量的構(gòu)造或工廠函數(shù)來完成組件配置看似避免了這一問題,但實際上不但可能破壞無侵入(non-invasive)原則,而且還可能使模型描述更加冗長。 同樣,如果試圖增強核心格式以減少這些缺點,則會犧牲其相應(yīng)的優(yōu)點。眾多的重型組件框架(如EJB,CCM等)采用的龐大XML描述格式和令人畏懼的 UML2.0及XMI就是前車之鑒。
解 決這個魚和熊掌兩難問題的一個有效方法是反其道而行之,干脆不去尋求一個能青菜蘿卜一刀切“為所有人解決所有問題”的建模格式。而是提供一個???放平 臺,允許用戶根據(jù)自己的特定需要決定取舍制定最佳建模格式,也就是所謂“針對問題域特定建模”DSM(domain specific modeling)。比如(參見PocoCapsule中dsm-gps例子 ),用戶可以為自己要描述的GPS專門量身定制一個DSM格式gps-device.dtd 。按照這個DSM格式,前面例子中的GPS系統(tǒng)可以被重新描述如下(參見setup.xml ):
與支持“通用目的建模”GPM(general purpose modeling)的核心格式(poco-application.context.dtd )比較,這個DSM格式(gps-device.dtd)不但抽象度高,而且定義更緊湊。在其模型描述中完全不涉及組件接口具體函數(shù)簽名,甚至連組件間具體的連接也被藏于幕后。而其配置參數(shù)的含義則一目了然。
如 前所說,XML框架中的XSLT和XQuery已經(jīng)為支持這個DSM格式準(zhǔn)備好了現(xiàn)成的平臺。這個平臺提供了以聲明方式描述模型之間互相轉(zhuǎn)換。因而,用戶 在定義了一個DSM后只需要再提供該DSM格式至另一個建模格式(比如IoC框架的核心建模格式)的轉(zhuǎn)換XSLT描述。那末,集成了XSLT轉(zhuǎn)換器的 IoC框架(比如PocoCapsule/C++ IoC and DSM框架 )將會按照指定的轉(zhuǎn)換描述將一個由新定義的DSM描述的結(jié)構(gòu)轉(zhuǎn)換為由另一個DSM定義的結(jié)構(gòu),直至遞歸到該IoC框架的核心格式結(jié)構(gòu)。不僅建模格式可以被 轉(zhuǎn)換,而且模型轉(zhuǎn)換的XSLT描述本身的格式也可以被轉(zhuǎn)換(甚至轉(zhuǎn)換的轉(zhuǎn)換的轉(zhuǎn)換),以簡化轉(zhuǎn)換描述的設(shè)計。這就是所謂的“高階轉(zhuǎn)換”HOT(higher order transformation )。關(guān)于IoC框架中DSM模型轉(zhuǎn)換的進(jìn)一步描述可參閱PocoCapsule DSM入門教材,用戶手冊 ,及代碼實例 。
一 個非侵入(non-invasive)IoC框架一視同仁地支持任何組件接口模型。DSM又使其能輕而易舉地支持任何用戶或標(biāo)準(zhǔn)組織定義的模型描述格式。 因此,一個IoC+DSM框架實際上是一個非常有效靈活的框架的框架。它讓菜鳥可以輕松且高質(zhì)量地實現(xiàn)很多組件框架,包括本文開始說的服務(wù)組件架構(gòu) SCA(參閱),以及軟件無線電SDR的JTRS-SCA核心框架(CF)組件架構(gòu)(參閱),各種機器人軟件組件架構(gòu)(參閱),CORBA組件架構(gòu)(參閱),等等如下圖所述的在PocoCapsule中提供的DSM框架。
1. IoC
IoC字面上的意思是“控制反轉(zhuǎn)”(Inversion of Control)。然而其具體含義五花八門的說法卻很容易讓人一頭霧水。這些說法往往是過多地關(guān)注IoC表面的甚至是字面的含義,卻忽略了IoC被用來解決的實質(zhì)問題(也無視了這個概念的歷史和使用現(xiàn)狀)。Martin Fowler 就把IoC闡述甚至更名為“依賴注入”(Dependency Injection )設(shè)計模式。而IoC鼻祖之一 Stefano Mazzocchi 卻指出Martin Fowler這個忽悠了一大批人的說法實際上是不得要領(lǐng)(Martin Fowler文章中???碼例子確實非常誤導(dǎo))。所以,這里并不急于給IoC下一個教條定義,而是從IoC的實質(zhì)目的開始探討。
IoC 的概念是Michael Mattson在1996年一篇討論面向?qū)ο罂蚣?Object Oriented Frameworks)的文章中提出的。面向?qū)ο笤O(shè)計及編程(OOD/OOP)的基本思想簡單地說就是把復(fù)雜軟件系統(tǒng)分解成通過接口相互合作的對象。這些 對象類的內(nèi)部實現(xiàn)之間并不互相牽扯,因而降低了問題的復(fù)雜性,且可獨立靈活地被重用和擴展。自然而然,經(jīng)典面向?qū)ο蟮木幊陶Z言(如C++,Java)的側(cè) 重點就是提供語言機制來方便并簡化這種基于對象類的分解,重用和擴展。然而,一個軟件系統(tǒng)的開發(fā)效率,可擴展性,以及部署維護(hù)升級的靈活性等并不完全由其 模塊化分解的程度和抽象的優(yōu)劣所決定。很大程度上,將這些分散部件有效地組裝成一個緊密合作的整體更 是決定該軟件項目成???及其產(chǎn)品系統(tǒng)優(yōu)劣的關(guān)鍵。
以 支持對象分解為己任的經(jīng)典面向?qū)ο笳Z言(如C++,Java)并沒有引入超越傳統(tǒng)命令式語言(imperative language)以外的系統(tǒng)組裝部署和配置手段(當(dāng)然,Java 5,C#現(xiàn)在都開始往這方面添料)。因而,雖然它們能夠有效地應(yīng)付底層子系統(tǒng)的拼裝和連接,但在進(jìn)行大范圍基于組件(既高層業(yè)務(wù)層模塊)層次的相應(yīng)作業(yè)時 就顯得簡陋,死板,冗長和低效。比如,在使用各種std的IO流類,STL容器類以及boost庫類這些底層模塊類時,采用語言本身的機制(自動變量或用 new算符)直截了當(dāng)?shù)貙嵗@些被使用類的對象就盡善盡美了。但在使用高層業(yè)務(wù)模塊時,為了避免對其具體實現(xiàn)類的依賴,人們不得不疊床架屋對語言機制進(jìn) 行額外的手工包裝。應(yīng)運而生的是一系列處理所謂管線邏輯(plumbing logic,既非業(yè)務(wù)邏輯)的設(shè)計模式,比如factory,builder,directory,adaptor,singleton, configuration/property manager,factory的manager甚至manager的manager等等。遺憾的是,一個世紀(jì)以來這些實際上是彌補語言缺陷的權(quán)宜之計反 而受到狂熱追捧而非深入反思。另外,在傳統(tǒng)軟件的設(shè)計和實現(xiàn)中,業(yè)務(wù)邏輯往往直接調(diào)用這些管線邏輯,從而破壞了業(yè)務(wù)邏輯的簡潔性和獨立性(比如增加了單元 測試的困難)。更重要的是,軟件組裝部署和配置的邏輯是支離破碎地散落混跡于各個業(yè)務(wù)邏輯組件中,既不直觀(往往是見樹不見林)也不靈活(牽一發(fā)則可能動 全身)。往往使得在宏觀結(jié)構(gòu)上理解,維護(hù),修改和擴充一個現(xiàn)有軟件要反而難于當(dāng)初從頭開發(fā)這個軟件。
為 解決以上問題,Michael Mattson提出了面向?qū)ο罂蚣艿腎oC設(shè)計原則。依照該原則,管線邏輯被轉(zhuǎn)移并集中至軟件框架內(nèi),業(yè)務(wù)邏輯模塊并不需知道更不必調(diào)用組件框架的服務(wù), 例如不用關(guān)心和調(diào)用其factory或lookup其directory或context等。軟件的組裝部署和配置完全是由管線邏輯框架反過來主動控制業(yè) 務(wù)邏輯模塊來安排。Michael Mattson用所謂的好萊塢原則(Hollywood Principle)“別來電(問)我,我會去電(告訴)你”(don't call me, I will call you)形象地比喻了這一設(shè)計思想。這個比喻中的“我”指的是負(fù)責(zé)管線邏輯的組件框架,“你”則是被其調(diào)遣配置的一個組件。比如,在一個業(yè)務(wù)邏輯模塊A需 要調(diào)用另一個業(yè)務(wù)邏輯模塊B的場景中,傳統(tǒng)的非IoC的設(shè)計(比如EJB2.0)是讓A調(diào)用管線邏輯(B的factory或某個directory服務(wù)) 來獲得B的引用(或指針)。在IoC框架內(nèi),框架不但完成A和B的實例化并保持追蹤,而且B的實例引用(或指針)也是由框架主動調(diào)用A的接口函數(shù)(比如構(gòu) 造及賦值函數(shù)等)賦予 A。從而,A的實現(xiàn)可以專著于業(yè)務(wù)邏輯,而管線細(xì)節(jié)(比如B的實例化及如何獲得其引用或指針)可以讓外部框架透明地安排妥當(dāng)。這種架構(gòu)完全避免了業(yè)務(wù)邏輯 對具體管線邏輯框架的牽連從而降低了業(yè)務(wù)邏輯模塊的復(fù)雜度,但更重要的是集中的組裝部署和配置邏輯為提高軟件宏觀結(jié)構(gòu)的直觀性和靈活性鋪平了道路(后面還 將具體討論)。
簡短概 括一下,從概念上說,IoC就是模塊化軟件組裝部署配置框架的一個設(shè)計原則。依該原則,業(yè)務(wù)邏輯模塊既不需要處理管線邏輯也可以對外部管線框架本身一無所 知(agnostic)。軟件的搭接完全由外部管線框架對業(yè)務(wù)邏輯模塊的主動操控來完成。從具體實現(xiàn)上說,除了一些開發(fā)工具以外,IoC框架不過是一個封 裝了必備的管線邏輯及IoC機制的輕型類庫(比如PocoCapsule/C++ IoC類 庫大約是70K)。從使用上說,用戶制作好業(yè)務(wù)邏輯組件(見下面非侵入性與POCO討論),并將軟件組裝及部署描述(見后面討論)提供給IoC框架(作為 IoC框架庫函數(shù)的???用參量,或直接驅(qū)動一系列庫函數(shù)調(diào)用)。IoC容器(被調(diào)用的庫函數(shù))將參照用戶的描述相應(yīng)地實例化和配置各個組件并將它們搭接 為所希望的部署狀態(tài)。
2. 非侵入性與POCO
對 于象C++和Java這類不支持動態(tài)類型的語言環(huán)境,一個很自然的問題就是業(yè)務(wù)邏輯組件需要支持什么樣的公共接口以使外部IoC框架能對其進(jìn)行操控。早期 的組件框架(比如Apache Avalon,EJB2.0,CORBA組件模型 CCM,JTRS-SCA等)幾乎清一色地采用侵入式(invasive)設(shè)計,也就是強制規(guī)定業(yè)務(wù)邏輯模塊(稱作bean)必須與特定的公共組件接口模 型兼容,既支持由組件框架定義的用來對組件進(jìn)行部署配置的公共接口類型或基類(base class)以及進(jìn)行實例化的所謂home接口。侵入式設(shè)計不僅學(xué)習(xí)和使用繁瑣(EJB2.0和CCM均是追求繁瑣,以繁瑣冒充強大的惡例),也大大地限制了組建框架的 開放性和適應(yīng)性。因為眾口難調(diào),所以幾乎每一個問題領(lǐng)域均定義了N個自己的組件框架和組件接口模型(比如機器人領(lǐng)域里就至少有10個)。侵入式設(shè)計導(dǎo)致組 件接口模型只能被其特定的框架所支持,從而形成了各自為政老死不相往來的組件框架孤島,限制了組件的重用范圍以及框架的通用性。大量這類侵入式組件框架以 及與之相應(yīng)的上下左右整個配套開發(fā)體系(如果僥幸有的話)均僅僅是在小范圍內(nèi)被采用,并以高成本在低水平上無謂地被重復(fù)開發(fā)和維護(hù)(CCM就是這方面最惡 名昭著的例子)。
因 此,現(xiàn)代IoC框架大都采用非侵入式(non-invasive)設(shè)計,也就是不對組件接口模型(即接口及函數(shù)簽名)做任何規(guī)定。換句話說,非侵入的 IoC框架一視同仁地支持任何組件接口模型,包括已經(jīng)被定義的和還未被定義的模型,也包括標(biāo)準(zhǔn)組織定義的或用戶自定義的模型。這些組件接口模型可以采用 (或不采用)任何公共或自定義接口,模板(template)或基類(也可以根本不是C++對象,比如C/C++函數(shù)),可以采用任何(合理的)實例化或 回收手段和部署配置函數(shù),包括構(gòu)造或析構(gòu)函數(shù),各種自定義 factory/pool/directory lookup函數(shù),以及各種全局或成員函數(shù)等(參見開源C++非侵入式IoC框架項目 PocoCapsule/C++ IoC的介紹 )。在非侵入的C++ IoC組件框架中,因為所有組件無論其接口模型的新舊美丑高矮胖瘦均被一視同仁地按平頭百姓對待,故均被統(tǒng)稱為“平庸C++對象”(Plain Old C++ Object )或POCO(相應(yīng)于Java中的“平庸Java對象”POJO)。
3. 軟件組裝及部署描述
在 使用IoC框架的C++軟件開發(fā)過程中,管線模塊及管線銜接代碼轉(zhuǎn)移并集中至IoC框架內(nèi),業(yè)務(wù)邏輯組件(高層模塊或庫類)的開發(fā)只需關(guān)注業(yè)務(wù)邏輯本身。 軟件的搭接由IoC容器(或庫類)按照用戶提供的軟件組裝及部署描述(assembly and deployment description)完成。因為由分散組件搭接成的軟件呈樹狀或更廣義的圖狀結(jié)構(gòu),所以,軟件組裝及部署描述就是對這種樹圖結(jié)構(gòu)模型的描述。這種描述 通常有三種表達(dá)(編寫)形式,既指令代碼(code),元數(shù)據(jù)(metadata),和用戶數(shù)據(jù)(data)。
雖 然經(jīng)典面向?qū)ο笳Z言(比如C++和Java)可以有效地按程序步驟構(gòu)造樹圖狀數(shù)據(jù)結(jié)構(gòu),但對結(jié)構(gòu)整體的“模型描述”卻蒼白無力。在C++中,要搭建一個樹 圖結(jié)構(gòu)的代碼不外乎就是一步步地去調(diào)用類似allocNode(),addNode(),wireNodes()等等函數(shù)。樹圖結(jié)構(gòu)模型在這種指令式的 (imperative)“步驟描述”中蕩然無存。對于管線邏輯比較簡單的應(yīng)用(例如底層或子模塊),結(jié)構(gòu)模型并不至關(guān)重要甚至是多此一舉,而步驟描述或 干脆拋棄IoC框架也許更直接了當(dāng)。然而對于管線邏輯較復(fù)雜的應(yīng)用,對結(jié)構(gòu)搭接具體步驟描述所引入的復(fù)雜性則與使用IoC框架抽象???線邏輯的原始初衷背道 而馳。
所謂元數(shù)據(jù)表述 就是以編程語言的元數(shù)據(jù)結(jié)構(gòu)來描述軟件的管線結(jié)構(gòu)模型。其實質(zhì)就是利用傳統(tǒng)指令性編程語言中類(class)結(jié)構(gòu)定義的聲明式語法來表達(dá)管線結(jié)構(gòu)。比如, 如果一個結(jié)構(gòu)有10個節(jié)點(組件),按此方法就是讓用戶定義一個包含10個相應(yīng)成員函數(shù)的類(class)并輔助以相應(yīng)注釋(annotation)標(biāo)識 管線連接。進(jìn)行軟件搭接時,IoC框架通過反射機制來解讀這個類的結(jié)構(gòu)并將其看做組件搭接的管線結(jié)構(gòu)描述。這種方法,看似提供了一種聲明式的 (declarative)模型描述,實際上則屬于一種牽強附會甚至是生搬硬套的kludge,除了能滿足“只使用編程語言本身來表述管線邏輯”這一教義 心態(tài)之外不具任何正面意義。
IoC 框架中有效靈活自然直觀的結(jié)構(gòu)描述形式恰恰就是被大牛們鄙視為惡俗的用戶文本數(shù)據(jù)描述形式。無論大牛們對這種C++和Java語法機制以外的方法如何深惡 痛絕,都不得不面對下面一個尷尬的窘境。C++和Java這類被他們(比如紅帽Jboss的首席科學(xué)家)奉為萬能銀彈的編程語言中并不提供對樹圖結(jié)構(gòu)整體 具體實例的有效描述手段。因為這類語言的目的僅僅是提供對象類的包裝抽象機制,而并不是提供具體多對象系統(tǒng)整體部署結(jié)構(gòu)的模型表述方法(更不要說對各種模 型之間變換,甚至變換的變換的聲明式描述)。
4. 基于XML的組裝及部署描述
主 流IoC框架(甚至很多傳統(tǒng)非IoC組件框架如EJB2.0和CCM)中用戶文本數(shù)據(jù)形式的組裝及部署描述大都是基于XML。XML的設(shè)計目的恰恰就是提 供對樹圖結(jié)構(gòu)的聲明式描述。對用戶來說,XML標(biāo)準(zhǔn)及技術(shù)成熟穩(wěn)定,已被普遍采用和支持(各種XML解析器和工具滿天飛)。另外,XML框架內(nèi)具備完善的 聲明式結(jié)構(gòu)轉(zhuǎn)換技術(shù)(XSLT,XQuery),為從底層通用IoC組件部署描述提高到“針對問題域的特定建模”DSM(domain specific modeling)準(zhǔn)備???了理想平臺。最重要的是,與SQL類似,XML是種???編程菜鳥都大呼容易的簡單直觀技術(shù),這就使得很多領(lǐng)域?qū)<夷軌驅(qū)M件構(gòu)成的 系統(tǒng)進(jìn)行搭建和部署。
下面以開源項目PocoCapsule/C++ IoC框架 中的一個具體例子來介紹這個方法。這個例子的完整代碼以及文檔在PocoCapsule源代碼包 和安裝包 中均可找到,也可以在http://www.pocomatic.com/docs/cpp-examples/basic-ioc/gps 上在線瀏覽。
這個例子中所要搭建部署的是一個如下圖所示包含定時觸發(fā)器(tick generator),GPS定位器(gps locator),導(dǎo)航???示器(navigate display)三個組件的GPS系統(tǒng)。

class EventListener {
public: virtual ~EventListener() {}
};virtual void refresh() = 0; class EventEmitter { public: virtual ~EventEmitter() {}
};virtual void subscribe(EventListener*) = 0; class TickGen : public EventEmitter { public: virtual void start() = 0;
};class GPSLocator : public EventEmitter, public EventListener { public: virtual int get_pos_x() = 0;
};virtual int get_pos_y() = 0; class NavDisplay : public EventListener { public:
};
|
這些組件的具體實現(xiàn)類TickGenImpl,GPSLocatorImpl和NavDisplayImpl則大致定義如下(忽略所有與組裝不相關(guān)細(xì)節(jié))。
class TickGenImpl : public TickGen
{ ...
public:TickGenImpl(int cnt, int interval);
};... class GPSLocatorImpl : public GPSLocator
{ ...
public:
GPSLocatorImpl();
... };
class NavDisplayImpl : public NavDisplay { ...
public:NavDisplayImpl(GPSLocator* loc) ;
}; ... |
這 些組件基類和具體實現(xiàn)類的定義以及它們的編譯連接(動態(tài)或靜態(tài)庫均可)等開發(fā)制作方式與一般C++應(yīng)用模塊沒任何區(qū)別,完全不需考慮IoC容器。它們甚至 可以是IoC時代以前已由第三方制作好,不提供源代碼的模塊。在這個例子中,除了需要用到這些組件實現(xiàn)類的構(gòu)造算子原型以外省略掉了其他所有不需要關(guān)心的 實現(xiàn)細(xì)節(jié)。
接下來,可以用PocoCapsule的XML語法來描述這個GPS應(yīng)用。PocoCapsule采用與Spring Framework盡量相近的XML文本格式(schema,定義在poco-application.context.dtd 中),并針對C++語言特征進(jìn)行了擴充。比如,將bean實例化后的setter調(diào)用普遍化為任何IoC調(diào)用。在這個文本格式中, 一個POCO組件的實例將被聲明為一個<bean>元素,包含其構(gòu)造算子的參數(shù)(<method-arg>),實例化后的IoC (<ioc>),以及IoC方法的參量(也是<method-arg>)等子孫元素。比如,這個例子中的GPS應(yīng)用結(jié)構(gòu)就可以由 下面一段XML聲明(見setup.xml ):
...
<poco-application-context> ... <bean class="TickGenImpl" lazy-init="false"> <method-arg type="short" value="10"/> <method-arg type="short" value="1"/> <ioc method="subscribe"> <method-arg ref="gps-locator"/> </ioc> <ioc method="start"/> </bean> <bean id="gps-locator" class="GPSLocatorImpl"> <ioc method="subscribe"> <method-arg ref="nav-display"/> </ioc> </bean> <bean id="nav-display" class="NavDisplayImpl"> <method-arg ref="gps-locator"/>
</bean> </poco-application-context> |
這段XML聲明簡單直觀地表達(dá)了三個組件實例以及它們之間的互相銜接結(jié)構(gòu)。可以形象化地將它用相應(yīng)的C++形式表示如下:
TickGenImpl* tick_gen = new TickGenImpl(10, 1);
tick_gen->subscribe(gps_locator); tick_gen->start(); ... GPSLocatorImpl* gps_locator = new GPSLocatorImpl; gps_locator->subscribe(nav_display); ... NavDisplayImpl* nav_display = new NavDisplayImpl(gps_locator); |
雖 然這兩種描述看似表達(dá)同樣的概念,但實際上他們有本質(zhì)區(qū)別。C++版本表達(dá)的是構(gòu)造這個應(yīng)用的具體先后步驟,因此實際上必須重新修改上面C++代碼行的次 序才能讓程序正常編譯和工作。而XML版本表達(dá)的則是一種結(jié)構(gòu),而并非構(gòu)造這個結(jié)構(gòu)的步驟。結(jié)構(gòu)中各<bean>節(jié)點的實例化次序與它們在 XML表述中的先后次序無關(guān),而是由IoC容器根據(jù)用戶聲明的節(jié)點屬性(比如lazy-init的值)以及銜接時依賴關(guān)系的先后來決定。
至此,用戶僅需要將這個XML描述以文件或字符串形式交給PocoCapsule/C++ IoC容器(既以文件名或XML字符串為參量調(diào)用PocoCapsule/C++ IoC庫函數(shù),見main.C ),讓其自動組裝部署所描述???應(yīng)用。關(guān)于PocoCapsule詳細(xì)的使用及工作機制描述可參閱其入門教材 ,用戶手冊 ,及代碼實例 。
5. DSM和模型變換
前 面一節(jié)例子中用到的XML文本格式是由PocoCapsule容器支持的核心格式(core schema)。因為XML在IoC框架中用于描述基于組件應(yīng)用的結(jié)構(gòu)模型,所以這個格式也稱為建模格式。該建模格式與其他IoC容器所采用的XML格式 大同小異。這種格式有直觀易學(xué),格式定義緊湊,普遍適用等優(yōu)點。然而,這些這些具有雙刃劍特征的優(yōu)點也意味著相應(yīng)的缺點,諸如直接引用組件編程接口函數(shù)簽 名,表述力和抽象度低,容易造成冗長的聲明及低級錯誤。比如,前面GPS例子中,XML模型描述直接涉及了有兩個參量的TickGenImpl的構(gòu)造算 子。從該模型描述中,人們無從判斷這兩個參量的目的和意義。如果該模型描述聲明了不匹配的參量類型,用戶可以僥幸地得到IoC容器異常報告(雖然可能難于 解讀)。但如果這兩個參量類型聲明無誤,而它們的數(shù)值在無意間被相互顛倒了(因為恰好是同一類型),那么用戶就只能聽天由命了。以setter函數(shù)取代多 參量的構(gòu)造或工廠函數(shù)來完成組件配置看似避免了這一問題,但實際上不但可能破壞無侵入(non-invasive)原則,而且還可能使模型描述更加冗長。 同樣,如果試圖增強核心格式以減少這些缺點,則會犧牲其相應(yīng)的優(yōu)點。眾多的重型組件框架(如EJB,CCM等)采用的龐大XML描述格式和令人畏懼的 UML2.0及XMI就是前車之鑒。
解 決這個魚和熊掌兩難問題的一個有效方法是反其道而行之,干脆不去尋求一個能青菜蘿卜一刀切“為所有人解決所有問題”的建模格式。而是提供一個???放平 臺,允許用戶根據(jù)自己的特定需要決定取舍制定最佳建模格式,也就是所謂“針對問題域特定建模”DSM(domain specific modeling)。比如(參見PocoCapsule中dsm-gps例子 ),用戶可以為自己要描述的GPS專門量身定制一個DSM格式gps-device.dtd 。按照這個DSM格式,前面例子中的GPS系統(tǒng)可以被重新描述如下(參見setup.xml ):
<gps-device>
<tick-generator use=”TickGenImpl” count=”10” interval=”1”/>
<gps-locator use=”GPSLocatorImpl”/>
<navigation-display use=”NavDisplayImpl”/>
</gps-device>. |
與支持“通用目的建模”GPM(general purpose modeling)的核心格式(poco-application.context.dtd )比較,這個DSM格式(gps-device.dtd)不但抽象度高,而且定義更緊湊。在其模型描述中完全不涉及組件接口具體函數(shù)簽名,甚至連組件間具體的連接也被藏于幕后。而其配置參數(shù)的含義則一目了然。
如 前所說,XML框架中的XSLT和XQuery已經(jīng)為支持這個DSM格式準(zhǔn)備好了現(xiàn)成的平臺。這個平臺提供了以聲明方式描述模型之間互相轉(zhuǎn)換。因而,用戶 在定義了一個DSM后只需要再提供該DSM格式至另一個建模格式(比如IoC框架的核心建模格式)的轉(zhuǎn)換XSLT描述。那末,集成了XSLT轉(zhuǎn)換器的 IoC框架(比如PocoCapsule/C++ IoC and DSM框架 )將會按照指定的轉(zhuǎn)換描述將一個由新定義的DSM描述的結(jié)構(gòu)轉(zhuǎn)換為由另一個DSM定義的結(jié)構(gòu),直至遞歸到該IoC框架的核心格式結(jié)構(gòu)。不僅建模格式可以被 轉(zhuǎn)換,而且模型轉(zhuǎn)換的XSLT描述本身的格式也可以被轉(zhuǎn)換(甚至轉(zhuǎn)換的轉(zhuǎn)換的轉(zhuǎn)換),以簡化轉(zhuǎn)換描述的設(shè)計。這就是所謂的“高階轉(zhuǎn)換”HOT(higher order transformation )。關(guān)于IoC框架中DSM模型轉(zhuǎn)換的進(jìn)一步描述可參閱PocoCapsule DSM入門教材,用戶手冊 ,及代碼實例 。
一 個非侵入(non-invasive)IoC框架一視同仁地支持任何組件接口模型。DSM又使其能輕而易舉地支持任何用戶或標(biāo)準(zhǔn)組織定義的模型描述格式。 因此,一個IoC+DSM框架實際上是一個非常有效靈活的框架的框架。它讓菜鳥可以輕松且高質(zhì)量地實現(xiàn)很多組件框架,包括本文開始說的服務(wù)組件架構(gòu) SCA(參閱),以及軟件無線電SDR的JTRS-SCA核心框架(CF)組件架構(gòu)(參閱),各種機器人軟件組件架構(gòu)(參閱),CORBA組件架構(gòu)(參閱),等等如下圖所述的在PocoCapsule中提供的DSM框架。
