Posted on 2008-11-23 11:57
canonical 阅读(1898)
评论(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的层面上都可以通过类似继承的操作实现局部调整,这同时也包括在不同的抽象层面上都能对模型进行合法性校验。