用Qi4j進行面向組合編程作者 Rickard Öberg譯者 羅小平 發(fā)布于 2009年1月4日 下午8時25分 長期以來,通過OOP對象集對領(lǐng)域概念進行建模的目標并未得到充分實現(xiàn)。那么迄今為止,我們?nèi)f般努力但難以解決的根本問題到底是什么?有沒有更好的解決辦法?在本文中我們將介紹面向組合編程(COP,Composite Oriented Programming)的概念,展示它如何規(guī)避OOP存在的一些問題,并重先點燃使用可重用部件組裝領(lǐng)域建模的希望。 問題
我為何物?實際中我可以有多重身份。某些時候,我是編寫軟件的開發(fā)者;另些時候,我是給大家講解某個有關(guān)Java話題的軟件開發(fā)者。但其他時候,我可能有完全不同的身份,比如銀行的客戶、大學(xué)的校友。簡而言之,我不同時候的身份,由當時的具體環(huán)境決定。在不同的環(huán)境中,我需要通過不同接口、以相應(yīng)行為與之交互。在所有這些環(huán)境中,我其實就是具有不同接口同一個對象。在我編寫軟件的時候銀行不會出現(xiàn)另一個我。 解決方案如果在軟件中用OOP為我建模,一些人會將我設(shè)計為一個Developer類。但這樣一個類顯然不能在不同時候表達我的不同身份(比如一個大學(xué)校友,因為 Developer類不包括社交概念)。因此對我建模的結(jié)果,可能會是幾個不同的類,或者在一個類中實現(xiàn)所有行為。在本文中,我們提出另一種方案:利用 Mixin(混入)概念完成這個實現(xiàn)。 組合首先,Mixin被實現(xiàn)為一個普通Java類,它通常實現(xiàn)一個特定接口,而這個接口將是Composite所要暴露接口的一部分。接著,我們用如下方法聲明一個Composite:創(chuàng)建一個Java接口,用注解聲明它要用到的Mixin,并且使用“extends”關(guān)鍵字指明需暴露哪些領(lǐng)域接口。通過這一方法,我們就可以載一個集中的地點明確定義Composite的結(jié)構(gòu)和行為。 使用COP時,雖然把橫切關(guān)注點保留在獨立的實現(xiàn)類中是一個不錯的主意,但是它們的裝配或組成方式應(yīng)該還是集中用Java接口來說明。為了避免出現(xiàn)重復(fù)的說明,我們可以通過“extends”關(guān)鍵字重用被擴展接口中的聲明。這樣,如果修改了被擴展接口,從其擴展而來的Composite接口聲明就會自動改變,不需我們逐個做人工修改。 利用這種辦法,我們相信可以做到兩全其美:存在于各個單獨實現(xiàn)類中的關(guān)注點實現(xiàn)了分離,每個類僅需關(guān)注特定的任務(wù);對于Composite最終應(yīng)該是什么樣的描述則是集中和確定的,這樣,Composite的開發(fā)者就可以全權(quán)負責(zé)定義中應(yīng)該包含的內(nèi)容。 代碼示例Composite在實際中如何具體應(yīng)用呢?讓我們看一個例子。假設(shè)我是一個Composite,那么可如下描述我: @Mixins({DeveloperMixin.class, SpeakerMixin.class, AlumniMixin.class}) public interface HumanComposite extends Developer, Speaker, Alumni, Composite {} 被擴展接口中包含了實際要被調(diào)用的方法,由Qi4j運行時環(huán)境去構(gòu)造一個Composite實例,這一實例可以將“來自客戶端的調(diào)用”路由給特定的 Mixin實例。但從客戶端的角度看,這個Composite實例完全是一個普通Java對象(盡管與普通OOP方法實現(xiàn)的領(lǐng)域?qū)ο蟊容^起來,它有更多的接口)。像Developer 這樣的領(lǐng)域接口是普通接口,和Qi4j無任何特殊關(guān)系,其實現(xiàn)本身也是實現(xiàn)了該接口的簡單Java類。但是上面所說的領(lǐng)域?qū)ο蟮纳矸菔怯?Composite實例而非某個Mixin實例定義的,這樣就解決了身份問題:對“我”這個對象的引用,可在系統(tǒng)范圍里傳遞,并被傳遞給在特定上下文環(huán)境中很有用的接口。如果引入更多領(lǐng)域或上下文環(huán)境時,也可通過擴展這個Composite來處理。 如需想創(chuàng)建另一個也使用Alumni接口的Composite及其實現(xiàn),我們可以讓此接口也擴展Alumni,并聲明使用相同的Mixin。因此,多重繼承和復(fù)用基礎(chǔ)類的常見問題也解決了。 LMM結(jié)構(gòu)軟件通常是在紙上分模塊、分層進行設(shè)計的。我們對類似如下的設(shè)計圖已經(jīng)非常熟悉了: 這個圖包含有多個模塊,不同模塊構(gòu)成了不同層,而各層又疊放在一起。我們可將這種設(shè)計方法簡稱為LMM(Layered Modules Metaphor,分層模塊表示法)。LMM圖可用來傳達一個整體應(yīng)用的總括,不讓我們陷入太多的細節(jié)當中。嚴格按照LMM的要求設(shè)計系統(tǒng),可減少系統(tǒng)缺陷、降低長期維護成本,這種系統(tǒng)對未來變更的反映也可更為靈活。絕大多數(shù)項目都使用LMM來描述應(yīng)用程序的構(gòu)成方式,許多項目設(shè)法遵循LMM,但只有很少的項目是按此執(zhí)行的。我想我們都已經(jīng)看到過很多慘痛教訓(xùn),比如在基礎(chǔ)結(jié)構(gòu)層的類中直接使用Web層的類。 Qi4j支持的結(jié)構(gòu)Qi4j目前已經(jīng)能為LMM提供明確的支持,這有助于規(guī)范團隊中開發(fā)人員的行為。Qi4j應(yīng)用結(jié)構(gòu)是一個小規(guī)則集:
看起來比較復(fù)雜,其實不然。本質(zhì)上,Composite實例在創(chuàng)建它們的模塊中都是私有的,除非顯式聲明為對模塊外或?qū)油夤_。這和在普通Java中使用“public”和“pirvate”修飾詞限定類的可見性是類似的。 Qi4j目前尚不提供其他可供選擇的結(jié)構(gòu),但其包含了構(gòu)造常用應(yīng)用程序結(jié)構(gòu)的簡單方法(包括一個層中只包含一個模塊的情況)。 結(jié)構(gòu)的使用領(lǐng)域代碼無需了解應(yīng)用程序結(jié)構(gòu),但可以用@Structure注解的形式出現(xiàn)。如下例所示: CompositeBuilderFactory將在創(chuàng)建時被注入Mixin,而且它僅允許代碼實例化結(jié)構(gòu)中可見的Composite。 結(jié)構(gòu)發(fā)揮作用的另一個常見例子發(fā)生在查找服務(wù)時。若在相同模塊中有且僅有一個要求類型的服務(wù),那么就無需引入額外裝配(Assembly)。服務(wù)的使用變得十分簡單。 例如,如果服務(wù)GenericInventory被聲明在Bread模塊中,那么每個inventory服務(wù)實例都將受其鄰近各自客戶端的程度約束。 應(yīng)用結(jié)構(gòu)的生成Qi4j 應(yīng)用需通過應(yīng)用程序代碼實現(xiàn)自舉,最簡單的啟動方法大致如下: 另外,還可利用SingletonAssembler編寫上述功能: SingletonAssembler是一個用于創(chuàng)建單層單模塊Qi4j應(yīng)用程序的工具類。 newApplication()方法也可接收Assembly[][][]類型的參數(shù),由此可創(chuàng)建“千層餅式”分層結(jié)構(gòu)的應(yīng)用環(huán)境(除了第一個和最后一個層,其他都有相鄰的上、下層)。例如: 上面代碼實現(xiàn)的結(jié)構(gòu)如下圖示: 最后,如果應(yīng)用程序結(jié)構(gòu)十分復(fù)雜,還可將ApplicationAssembly實例作為參數(shù)傳遞給newApplication()方法。 ApplicationAssembly用類似迭代的形式創(chuàng)建全部LayerAssembly,再為每個LayerAssembly創(chuàng)建 ModuleAssembly。舉例如下: 運行上面代碼可得到如下結(jié)構(gòu): 結(jié)構(gòu)化的好處通過代碼顯式實現(xiàn)應(yīng)用程序結(jié)構(gòu)有兩個顯著的好處:
這意味著,越近的Composite優(yōu)先級越高,越易訪問;外部不能訪問模塊或?qū)觾?nèi)私有的Composite。因為服務(wù)被實現(xiàn)為Composite,其解析方法更為含蓄,需要的裝配配置也少得多。 Qi4j結(jié)構(gòu)概念的另一個有趣之處在于每個應(yīng)用都有一個靜態(tài)結(jié)構(gòu)組合,它可以通過工具來抽取并展示,而不必單獨維護。這使得架構(gòu)師、設(shè)計師或團隊負責(zé)人可輕松跟蹤開發(fā)人員對架構(gòu)的遵循情況,很容易找到越軌者。 總結(jié)本文通過實現(xiàn)于Java平臺的Qi4j,簡要討論了COP的可行性。我們看到以傳統(tǒng)OOP觀念實現(xiàn)對象的Composite,是如何更好分離關(guān)注點(concerns),從而提升代碼的質(zhì)量和復(fù)用性的。此外,我們也討論了顯式建模應(yīng)用結(jié)構(gòu)的思路,這一結(jié)構(gòu)通常只在紙面上而不是在代碼中定義。通過顯式建模應(yīng)用結(jié)構(gòu),我們可以更容易落實架構(gòu)執(zhí)行并消除服務(wù)間的相互依賴。這將幫助我們創(chuàng)建更大規(guī)模的系統(tǒng),而且隨著引入越來越多的組件及服務(wù),我們的系統(tǒng)不至被其自身的“重量”壓垮。 最后要強調(diào)的一點是,COP和Qi4j中的絕大多數(shù)理念并非前無古人。我們現(xiàn)在做的,恰恰是在前人編程實踐和各種框架中尋找各種優(yōu)秀的思想和模式,并提煉出那些我們認為在編寫軟件及保持容易理解且易于維護方面都能給開發(fā)者提供幫助的內(nèi)容。無論是開發(fā)軟件還是我們的日常生活中,將古老的東西運用于新的環(huán)境都是非常重要的。 作者簡介Rickard Öberg曾參與過多個J2EE開源項目的開發(fā)工作,如JBoss、XDoclet和WebWork。他也是SiteVision CMS/portal平臺(以AOP為基礎(chǔ))的首席架構(gòu)師?,F(xiàn)在服務(wù)于Jayway,主要關(guān)注方向是在新一輪以互聯(lián)網(wǎng)為中心的應(yīng)用中廣泛采用的面向領(lǐng)域軟件開發(fā)技術(shù)。 Jayway 簡介Jayway是瑞典一家擁有90位認證Java專家的一流Java公司。我們的服務(wù)范圍包括:內(nèi)部開發(fā)、專業(yè)咨詢以及Java平臺相關(guān)指導(dǎo)與培訓(xùn)。我們對開源軟件有堅定信念,并正為大量開源項目積極工作。我們珍視知識的交流與共享。Jayway的網(wǎng)站是www.。 |
|