Posted on 2008-11-23 11:57
canonical 閱讀(1919)
評論(0) 編輯 收藏 所屬分類:
設計理論
代碼生成(Code Generation)本身是一個非常宏大的概念。從某種意義上說,當我們明確了計算的意義之后,所做的一切都只是一系列代碼生成的過程,最終的目標是生成某種可執行的機器碼。對web程序員來說,代碼生成是最熟悉不過的了,每天我們所做的工作就是JSP=>Servlet=>HTML。不過,現在多數人腦海中的代碼生成,指的一般只是根據配置輸出一個或多個程序文本的過程,最常見的是根據數據庫模型生成增刪改查相關代碼。這種技術其實很少在小型以上的項目中起到積極的作用.因為一般的生成工具都沒有實現追加功能,無法適應模型的增量修改。此外一般生成的代碼相比于手工書寫的代碼要更加冗長,需要被直接理解的代碼總量不降反升.為圖一時之快,所要付出的是長期的維護成本。
在應用開發中,有些領域是非常適合于使用代碼生成技術的。例如根據領域模型生成ORM(對象-關系映射)描述,或者根據接口描述生成遠程調用代理/存根 (Proxy/Stub)等。因為它們實際上只是對同一信息的不同技術形式或者不同技術層面的同義反復而已。這種生成最理想的方式是動態進行,可以隨時保持模型的有效性。RoR(RubyOnRails)框架中ActiveRecord技術便是一個成功的范例,它甚至提供了動態生成的DAO函數,減少了一系列的包裝調用過程。
代碼生成更加深刻的應用是完成高層模型向低層模型的轉化,這一過程往往是非平凡(non-trivial)的。在Witrix平臺中通過代碼生成來支持領域抽象,可以用非常低的成本跨越結構障礙,將自定義的領域模型嵌入到現有的技術體系中。這其中我們的主要工作是解決了生成代碼與手工書寫代碼之間的有效隔離及動態融合問題,確保代碼生成可以反復的以增量的方式進行,同時支持最細粒度處對生成的代碼進行定制調整。
舉一個簡單的例子,假設現在需要開發一個三步審批的流程,每一步的操作人可以錄入意見,可以選擇通過或者回退,可以選擇下一步操作的具體操作人,系統自動記錄操作時間,每個操作人可以查看自己的操作歷史等。雖然在現有技術體系中實現這一功能需要不少代碼,但是在業務層面上描述這一功能并不需要很多文字,實際需要提供的信息量很小。顯然,建立領域模型是比較適合的做法,可以定義一種DSL(Domain Specific Language)來描述這一模型。
<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>
以上功能涉及到多個操作場景,實現的時候需要補充大量具體信息,其中很大一部分信息來自于背景知識,例如顯示樣式,界面布局,前后臺通信方式等。以上模型可以進一步抽象為如下標簽
<flow_cp:StepFlow3/>
在不同應用中復用以上流程邏輯的時候可能需要局部修正,例如
<flow_cp:StepFlow3>
<step id="check" userField="checker" />
</flow_cp:StepFlow3>
更加復雜的情形是DSL本身提供的抽象無法滿足全部需求,而需要在局部補充更多模型之外的信息,例如物品接收單審批通過后自動導入庫存等。
在Witrix中,代碼生成不是直接產生最終的輸出,而是在編譯期生成基礎模型,它與補充描述通過extends算子進行融合運算之后產生最終輸出, 這種融合可以實現基礎功能的新增,更改或者刪除。典型的調用形式為
<biz-flow>
<extends>
<flow_cp:StepFlow3>
<step id="check" userField="checker" />
</flow_cp:StepFlow3>
</extends>

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