锁定老帖子 主题:从编写代码到制造代码
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (2)
|
|
---|---|
作者 | 正文 |
发表时间:2009-02-15
软件开发作为一种工程技术,它所研究的一个重点就是如何才能有效降低软件产品的研发成本。在这一方向上,组件技术取得了空前的成功。它所提供的基本图景是像搭积木一样从无到有的组装出最终的产品。在某种意义上,这是对现代建筑工业的模仿和致敬。新材料,预制件,框架结构,这些建筑学的进展在软件领域被一一复制,建筑工地上的民工自然也成为了程序员们学习的楷模。毕竟,在组件的世界中码代码,基本上也是一种“搬砖”的行为。
BaseClass<ArgClass> --> CodeGenerator<DSL> DSL(或者某种模型对象)相对于普通的类型(Class),信息密度要大很多,它可以提供更丰富也更完整的输入信息。而CodeGenerator也不必拘泥于基础语言本身提供的各种编译机制,而是可以灵活应用各种文本生成技术。http://canonical.iteye.com/blog/309395 CodeGenerator在这里所提供的正是根据输入模型推导出完整实现代码的构造原理。
修正过程 ==> CodeGenerator<DSL>
为了改进以上代码生产过程,一些人试图在CodeGenerator中引入越来越多的可配置性,将各种变化的可能内置在构造原理中。显然这会造成CodeGenerator的不正常的肿胀。当更多的偶然性被包含在原理中的时候,必然会破坏原理的简单性和普适性。 输入信息 + 一段用于推导的原理 + 修正补充 = 真实模型 必须存在[修正补充]这一项才能维持以上方程的持久平衡。
Biz ==[AOP extends]==> CodeGenerator<DSL>
继承仅仅能够实现同名方法之间的简单覆盖,而AOP所代表的技术原理却是在代码结构空间中进行任意复杂的删改操作,它潜在的能力等价于人工调整。
Context0 + DSL1 + EXT0 = DSL0 Context1 + DSL2 + EXT1 = DSL1 ... http://canonical.iteye.com/blog/33824 Witrix平台中BizFlow可以看作是对DaoWebAction的修正模型,但是它本身具有完整的意义,可以直观的被理解。在BizFlow的基础上可以逐步建立SeqFlow,StateFlow等模型。http://canonical.iteye.com/blog/126467 现在有些人试图深挖函数式语言,利用模式匹配之类的概念,做符号空间的全局优化。但是我们需要认识到通用的机制是很少的,能够在通用语言背景下被明确提出的问题更是很少的。只有在特定领域中,在掌握更多局部信息的情况下,我们才能提出丰富的问题,并作出一定背景下的解答。DSL的世界中待做的和可做的工作很多。http://canonical.iteye.com/blog/147065
声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2009-03-06
为了摆脱人工修正过程,将模型调整纳入到概念世界中,我们需要超越继承机制的,更加强大的,新的技术手段。其实在当前的技术背景下,这一技术已经是呼之欲出了。这就是AOP, Aspect Oriented Programming。
继承机制其实是一种非常僵化和原始的方式,AOP也只是提供了适合特定模式的描述方式,函数式也并非万能药。 简单举一个例子,就继承而言,继承实际上是一种缩减的表达: A继承B,A是在B的基础上修改如下函数而得到的。 当我们这样看待继承的时候,一种继承关系就是一种描述,而这种描述本身,也是可以被继承的。 |
|
返回顶楼 | |
发表时间:2009-03-06
uda1341 写道 为了摆脱人工修正过程,将模型调整纳入到概念世界中,我们需要超越继承机制的,更加强大的,新的技术手段。其实在当前的技术背景下,这一技术已经是呼之欲出了。这就是AOP, Aspect Oriented Programming。
继承机制其实是一种非常僵化和原始的方式,AOP也只是提供了适合特定模式的描述方式,函数式也并非万能药。 简单举一个例子,就继承而言,继承实际上是一种缩减的表达: A继承B,A是在B的基础上修改如下函数而得到的。 当我们这样看待继承的时候,一种继承关系就是一种描述,而这种描述本身,也是可以被继承的。 动态语言生成代码比AOP之流容易多了,加上作为一等公民的函数对象,"Write code that writes code"非常容易。 严格的说,Code Generation分为运行时生成和源代码生成。 脚本语言短小,无需编译,利用脚本语言作源代码生成也越来越流行。 很多重复性的工作如翻译已有的接口成另一种语言,集成数据到代码中……都可以利用源代码生成。 举个Ruby生成C源代码的简单例子: class CGenerator def int var, val puts "int #{var} = #{val};" end end c = CGenerator.new c.instance_eval <<BLOCK var = 'a' val = 1 while var != 'z' int var, val var.succ! val += 1 end BLOCK 结果: int a = 1; int b = 2; int c = 3; int d = 4; int e = 5; int f = 6; int g = 7; int h = 8; int i = 9; int j = 10; int k = 11; int l = 12; int m = 13; int n = 14; int o = 15; int p = 16; int q = 17; int r = 18; int s = 19; int t = 20; int u = 21; int v = 22; int w = 23; int x = 24; int y = 25; 当然这个例子可能不太实用,不过继续扩展CGenerator,可以给编写带重复性的源代码省下不少工夫。 再高级一点的实践,就是将Code Generator做成MVC的形式,读入数据,处理,然后填入模版,时间所限就不举例了。 有兴趣可以看看 http://www.codegeneration.net/ |
|
返回顶楼 | |
发表时间:2009-03-07
当我提到AOP的时候,我所指的决不是现有的AOP技术实现,而只是AOP所代表的技术概念。
动态语言代码生成最后所提供的效果也在AOP所涵盖的范畴之内。 AOP就是对程序代码结构的一种动态修正技术. 代码生成决不仅是代码生成工具,或者是动态脚本代码生成,或者是动态字节码代码生成,或者是JIT代码生成. 我对代码生成的关注重点在于生成的过程中如何保持概念边界,如何将它作为模型定义和转换的手段. |
|
返回顶楼 | |
发表时间:2009-03-07
当我提到动态语言的时候,我所指的绝不是现有的动态语言技术实现,而只是动态语言所代表的技术概念。
但是不好意思,当我提到AOP的时候,我所指的就是像AspectXXX那样的东西…… 对于你说的AOP,再次不好意思,我抽象思维能力比较弱……不是很明白为什么这么多东西都被AOP(如果我没记错,面向方面编程?)覆盖了? 现有的元编程技术已经非常强大了吧? 像boost.spirit,就可以动态添加语法规则; 动态语言实现的DSL也可以玩一个无穷序列,甚至还可以用DSL生成DSL生成DSL…… 至于如何划分边界,我想基于parse tree的东西已经把事情弄得很清晰了。 如果要说谁更牛逼一点,我想可以证明它们是等价的…… |
|
返回顶楼 | |
发表时间:2009-03-07
我的意思不是说代码生成是AOP的一部分,我是说你提到的代码生成比AOP容易多了. 用代码生成构造结构, 用AOP手段一样可以达到而已.
"我想基于parse tree的东西已经把事情弄得很清晰了" 确实,从概念上这些东西都是非常清楚的. AOP就是一种定位组装技术,元编程就是一种代码生成. 但是概念上很清楚不代表你找到了新的应用方式. 除了别人已经做好的例子, 你能否想出什么复杂的应用实例? 如何具体应用这些概念? |
|
返回顶楼 | |
发表时间:2009-03-07
在我的文章里所表达的一个核心概念在于 代码生成并不必然是完整的最终的结构, 还需要通过AOP手段进行精细的调节, 而这个调节过程本身可以进一步被抽象. 无论是基础的结构,还是 AOP导入的aspect都可以进一步被分解, 用其他的DSL来描述.
|
|
返回顶楼 | |
发表时间:2009-03-07
最后修改:2009-03-07
canonical 写道 我的意思不是说代码生成是AOP的一部分,我是说你提到的代码生成比AOP容易多了. 用代码生成构造结构, 用AOP手段一样可以达到而已.
"我想基于parse tree的东西已经把事情弄得很清晰了" 确实,从概念上这些东西都是非常清楚的. AOP就是一种定位组装技术,元编程就是一种代码生成. 但是概念上很清楚不代表你找到了新的应用方式. 除了别人已经做好的例子, 你能否想出什么复杂的应用实例? 如何具体应用这些概念? 举几个自己应用代码生成的例子,虽然写得不怎么样,但是觉得比用AOP容易得多 下面是一个消息处理函数的生成模版,msg_listeners通过解析其他文件得到。 (写这段函数的初衷是不需要Thunk Hack也能避免产生庞大的虚函数表) 在VC里设置了Prebuild event,每次build之前先Generate一趟(用ant,make,rake也差不多)。 一点说明:C的宏就是一种元编程,这里扩展了macro的语法,'#'开始的行除了原有的C macro之外,都被当做是ruby代码处理。这种处理方式也非常容易重用。 '${}'为嵌入相应对象的值。 #static: LRESULT CALLBACK _wnd_proc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) { LRESULT result = 0; switch(message) { #msg_listeners.each do |msg,listeners| case ${msg}: #listeners.each do |listener| if(${fname_base}.${listener}) return result; #end break; #end //generate mouseover and leave events case WM_NCHITTEST: if(window_base::h_mouseover != hwnd) { SendMessage(window_base::h_mouseover, WM_MOUSELEAVE, 0, 0); SendMessage(hwnd, WM_MOUSEHOVER, 0, 0); window_base::h_mouseover = hwnd; return 0; } break; } //reflect unprocessed notify if (message == WM_NOTIFY) { return SendMessage(((LPNMHDR)lparam)->hwndFrom, WM_RNOTIFY, wparam, lparam); } #if child_window return CallWindowProc( _old_proc, hwnd, message, wparam, lparam ); #else return DefWindowProc( hwnd, message, wparam, lparam ); #end } 下面是根据Scintilla.iface生成Ruby extension接口的Erb模版的一部分 (Scintilla的接口太多了,一个个写肯定累死人,有些细节还是不能依赖swig完成,只好自己写了) Can AOP do this ? <% strres_funs.each do |f| %> static VALUE _x_<%= f[:fun] %>(<%= proto(f[:p1],f[:p2]) %>) { VALUE v; int len = _SEND(<%= f[:code] %>, <%= cval(f[:p1]) || '0' %>, 0); char* buf = _x_arena_alloc(len); _SEND(<%= f[:code] %>, <%= cval(f[:p1]) || 'len' %>, buf); v = rb_str_new(buf, (len>0 ? len-1 : 0)); rb_funcall(v, id_force_encoding, 1, string_utf_8); return v; } <% end %> 动态类型,动态方法的好处是:它们都非常直观。 而AOP?什么是advice?什么是pointcut?我需要学习这些才能修改它们么? 举个例子,升级到ruby1.9,很多gem都报错:找不到"xxx".each方法。那么很简单: class String alias each each_byte end 打开类修改,定义一个别名,问题解决 可能我举的例子过于简单,但是问题没办法变得更复杂…… 或者设想一个庞大的工程,需要大量补镬的场景? 那么应用AST做点剪枝工作吧,我觉得ANTLR官网上很多文章都不错的。 这样生成也有缺点:不能改生成的目标代码。 但是,代码生成就是一个编译的过程,正如人们应该修改.java文件,而不是去修改.class文件,如果要改,就应该改源文件。 |
|
返回顶楼 | |
发表时间:2009-03-07
canonical 写道 在我的文章里所表达的一个核心概念在于 代码生成并不必然是完整的最终的结构, 还需要通过AOP手段进行精细的调节, 而这个调节过程本身可以进一步被抽象. 无论是基础的结构,还是 AOP导入的aspect都可以进一步被分解, 用其他的DSL来描述.
我的想法是不必要在“最终的结构”和“精细的调节”上面纠结, 代码生成就是编译过程,只要分离好生成代码和非生成代码即可。 新建太多概念意义也不大。 |
|
返回顶楼 | |
发表时间:2009-03-07
不是要新建概念,而是不要仅在别人告诉我们的知识中徘徊。
假设现在我已经写好了一段代码生成器的实现,可以生成我需要的代码,例如你上面_wnd_proc的实现。 我在几个常见的应用中也用到了,可是突然有一天有一个需求,要对一个特定的msg='my_message'的时候做一个特殊处理。现在应该如何处理呢? 是重写代码生成器,或者为代码生成器增加一个配置选项,还是利用某种AOP技术直接操作生成的代码做一点修正? “最终的结构”和“精细的调节”不只是某种空泛的概念区别,而是对应着不同的实现策略。 “分离好生成代码和非生成代码即可”这种分离到底能做到多大的程度?是简单的代码交错,还是复杂的如同C++模板那种非常深入的编译期的递归交织。一个信息表达A到底能够以哪些方式传播到系统中的哪些部分? |
|
返回顶楼 | |