代碼生成(Code Generation)本身是一個(gè)非常宏大的概念。從某種意義上說(shuō),當(dāng)我們明確了計(jì)算的意義之后,所做的一切都只是一系列代碼生成的過(guò)程,最終的目標(biāo)是生成某種可執(zhí)行的機(jī)器碼。對(duì)web程序員來(lái)說(shuō),代碼生成是最熟悉不過(guò)的了,每天我們所做的工作就是JSP=>Servlet=>HTML。不過(guò),現(xiàn)在多數(shù)人腦海中的代碼生成,指的一般只是根據(jù)配置輸出一個(gè)或多個(gè)程序文本的過(guò)程,最常見的是根據(jù)數(shù)據(jù)庫(kù)模型生成增刪改查相關(guān)代碼。這種技術(shù)其實(shí)很少在小型以上的項(xiàng)目中起到積極的作用.因?yàn)橐话愕纳晒ぞ叨紱]有實(shí)現(xiàn)追加功能,無(wú)法適應(yīng)模型的增量修改。此外一般生成的代碼相比于手工書寫的代碼要更加冗長(zhǎng),需要被直接理解的代碼總量不降反升.為圖一時(shí)之快,所要付出的是長(zhǎng)期的維護(hù)成本。
在應(yīng)用開發(fā)中,有些領(lǐng)域是非常適合于使用代碼生成技術(shù)的。例如根據(jù)領(lǐng)域模型生成ORM(對(duì)象-關(guān)系映射)描述,或者根據(jù)接口描述生成遠(yuǎn)程調(diào)用代理/存根 (Proxy/Stub)等。因?yàn)樗鼈儗?shí)際上只是對(duì)同一信息的不同技術(shù)形式或者不同技術(shù)層面的同義反復(fù)而已。這種生成最理想的方式是動(dòng)態(tài)進(jìn)行,可以隨時(shí)保持模型的有效性。RoR(RubyOnRails)框架中ActiveRecord技術(shù)便是一個(gè)成功的范例,它甚至提供了動(dòng)態(tài)生成的DAO函數(shù),減少了一系列的包裝調(diào)用過(guò)程。
代碼生成更加深刻的應(yīng)用是完成高層模型向低層模型的轉(zhuǎn)化,這一過(guò)程往往是非平凡(non-trivial)的。在Witrix平臺(tái)中通過(guò)代碼生成來(lái)支持領(lǐng)域抽象,可以用非常低的成本跨越結(jié)構(gòu)障礙,將自定義的領(lǐng)域模型嵌入到現(xiàn)有的技術(shù)體系中。這其中我們的主要工作是解決了生成代碼與手工書寫代碼之間的有效隔離及動(dòng)態(tài)融合問(wèn)題,確保代碼生成可以反復(fù)的以增量的方式進(jìn)行,同時(shí)支持最細(xì)粒度處對(duì)生成的代碼進(jìn)行定制調(diào)整。
舉一個(gè)簡(jiǎn)單的例子,假設(shè)現(xiàn)在需要開發(fā)一個(gè)三步審批的流程,每一步的操作人可以錄入意見,可以選擇通過(guò)或者回退,可以選擇下一步操作的具體操作人,系統(tǒng)自動(dòng)記錄操作時(shí)間,每個(gè)操作人可以查看自己的操作歷史等。雖然在現(xiàn)有技術(shù)體系中實(shí)現(xiàn)這一功能需要不少代碼,但是在業(yè)務(wù)層面上描述這一功能并不需要很多文字,實(shí)際需要提供的信息量很小。顯然,建立領(lǐng)域模型是比較適合的做法,可以定義一種DSL(Domain Specific Language)來(lái)描述這一模型。
<flow_cp:SeqFlow>
<step id="draft" userField="draferId" dateField="draftTime" waitStatus="drafted" />
<step id="check" userField="checkerId" dateField="checkTime" opinionField="checkOpinion"
waitStatus="sent" />
<step id="approve" userField="approverId" dateField="approveTime"
opinionField="approveOpinion" waitStatus="checked" passStatus="approved" />
</flow_cp:SeqFlow>
以上功能涉及到多個(gè)操作場(chǎng)景,實(shí)現(xiàn)的時(shí)候需要補(bǔ)充大量具體信息,其中很大一部分信息來(lái)自于背景知識(shí),例如顯示樣式,界面布局,前后臺(tái)通信方式等。以上模型可以進(jìn)一步抽象為如下標(biāo)簽
<flow_cp:StepFlow3/>
在不同應(yīng)用中復(fù)用以上流程邏輯的時(shí)候可能需要局部修正,例如
<flow_cp:StepFlow3>
<step id="check" userField="checker" />
</flow_cp:StepFlow3>
更加復(fù)雜的情形是DSL本身提供的抽象無(wú)法滿足全部需求,而需要在局部補(bǔ)充更多模型之外的信息,例如物品接收單審批通過(guò)后自動(dòng)導(dǎo)入庫(kù)存等。
在Witrix中,代碼生成不是直接產(chǎn)生最終的輸出,而是在編譯期生成基礎(chǔ)模型,它與補(bǔ)充描述通過(guò)extends算子進(jìn)行融合運(yùn)算之后產(chǎn)生最終輸出, 這種融合可以實(shí)現(xiàn)基礎(chǔ)功能的新增,更改或者刪除。典型的調(diào)用形式為
<biz-flow>
<extends>
<flow_cp:StepFlow3>
<step id="check" userField="checker" />
</flow_cp:StepFlow3>
</extends>

<action id="pass_approve">
.
</action>
</biz-flow>
這里的操作過(guò)程可以看作是BizFlow extends SeqFlow<FlowConfig extends StepFlow3Config>,與泛型技術(shù)非常類似,只是需要更強(qiáng)的局部結(jié)構(gòu)控制能力。
按照級(jí)列理論
http://canonical.javaeye.com/blog/33824 ,我們可以定義一個(gè)DSL的級(jí)列,整個(gè)抽象過(guò)程為
Context0 + DSL1 + EXT0 = DSL0
Context1 + DSL2 + EXT1 = DSL1
在目前一些通用語(yǔ)言中,也有一些所謂內(nèi)嵌DSL的方案,可以提供比較簡(jiǎn)潔的業(yè)務(wù)描述。但是僅僅建立DSL描述是不充分的,從級(jí)列理論的觀點(diǎn)看,我們必須提供一種DSL的補(bǔ)充手段,能夠在細(xì)節(jié)處補(bǔ)充DSL模型之外的信息,實(shí)現(xiàn)兩者的自然融合。同時(shí)我們應(yīng)該可以在不同的抽象層面上獨(dú)立的進(jìn)行操作,例如在 DSL1和DSL2的層面上都可以通過(guò)類似繼承的操作實(shí)現(xiàn)局部調(diào)整,這同時(shí)也包括在不同的抽象層面上都能對(duì)模型進(jìn)行合法性校驗(yàn)。