posts - 176, comments - 240, trackbacks - 0, articles - 7

       代码生成现在已经逐渐成为软件开发中的一种标准技术,在众多的软件领域都大大减轻了我们重复劳动的工作量。程序中总是存在着这样那样的隐蔽的关联,它们无法在通用的程序语言框架下得到明确的表达,代码生成是我们突破既定的语言和框架限制的一种重要手段。但是代码生成也存在着严重的弊病,一方面一般的程序语言在设计时没有考虑到和代码生成工具的相互配合,因此生成代码是一次性的,代码生成工具无法以增量的方式修正已经生成的代码。另一方面,程序的结构是复杂的,代码生成工具一般基于某种简化的通用的程序模型(例如CRUD)来产生代码,它无法承载完整的程序结构,因此代码生成后手工调整量仍然很大,有的时候甚至为了微小的界面调整,将生成的代码修改的面目全非,无法发挥代码生成的优势。
       在witrix平台中主要使用meta generation而不是code generation. meta实际上是对一种定制模型(model)的描述,它在某种意义上可以看作是完整程序的简化版本,但它本身并不意味着最终的程序结构。在witrix平台各处meta的使用都是可选的, 特别是在多变的前台页面,我们可以选择根据meta描述自动生成界面,也可以选择通过<df:Field name="字段名"/>来引用单个字段的meta数据. 在witrix平台中, meta可以看作是系统运行的内核, 它通过syncWithModel等属性与设计工具发生耦合. 当设计模型修改之后, 这种修改能够以增量的形式通过可控制(修改)的信道传播到系统各处.

posted @ 2007-01-21 18:11 canonical 阅读(2514) | 评论 (5)编辑 收藏

    目前的AOP(Aspect Oriented Programming)技术虽然以动态代码织入为核心,但是这种织入仍然是一次性的。一般在系统构造的时候(例如ClassLoader装载Class的时候)实现类和成员函数的增强。此后在运行时刻代码结构是固定的而不再发生变化。但是在真正的业务处理过程中,我们在不同的应用场景下可能要求织入不同的Aspect。例如基本的权限Aspect, 如果在不同的应用场景有不同的权限设定,则我们显然希望进入一个确定的操作场景的时候就指定一整套的权限策略,而不是在每个函数调用时刻写上一大堆的if/else(这种分离的条件判断正是AOP试图从结构上消除的)。
    为了实现AOP的二级动态化,我们首先需要约定一些公共标记(坐标),便于在标记处插入Aspect Container, 其次我们需要在系统中建立一个隐蔽的信道,可以通过该信道传递一个标志符(Aspect的id),用于在各处选择特定的Aspect. 建立这种动态特性之后,我们就可以据此发展出Aspect组的概念,并实现Aspect组之间的继承关系等高阶结构,从而最大限度的限制程序结构的分散化。
    Witrix平台的BizFlow设计在概念上可以看作是AOP的一种二级动态化织入设计,它通过$bizId这一特定参数来选择织入的Biz。一个BizFlow对象是一组Biz(Aspect)的集合, BizFlow可以通过extends机制实现集合之间的合并等(BizFlow实现的合并策略其实是非常复杂的)。一个简单的应用就是流程支持,例如一个普通的实体对象对应的bizflow只需要加上如下代码即可获得流程相关的代码,前台菜单等。
  <bizflow extends="testflow.biz.xml">

posted @ 2007-01-14 17:04 canonical 阅读(1085) | 评论 (2)编辑 收藏

  几年前Michael Atiyah受邀在浙江大学做过一个讲演,题目是Mathematics in the 20th Century,
http://www.cnw3.org/smth/Mathematics/historiesandmathmaticians/goodessays/00000035.htm, 在其中他回顾了二十世纪的主要的数学发展。被他列在第一条的进展就是From Local To Global. 在传统上,数学的主要研究对象是一些得到显性表达的局部公式,而拓扑学对于整体性的“不变性质”的研究最终将我们对于数学和物理学的理解推进到一个新的高度。我想在其他领域中,这种认识上的深化也将是一个必然的过程。随着AOP这种大范围结构操纵技术的兴起,软件技术是否也发展到了可以对程序的整体结构做一些反思的时候?
  面向对象有什么用?它是在各个层面都可以使用的一种描述工具。从一些早期的文献我们可以看出一些端倪,一种整体性的均一的概念是我们迫切需要的。对象可以构成对象,Everything is Object. 只是因为我们对这些太熟悉以致于在今天看来显得有些陈腐。很多人现在津津乐道于CoC(Convention over Configuration)作为一种局部程序设计技巧所带来的可以少些一些代码的经济性,却没有看到CoC更大的作用在于在大范围内保持了程序结构的一致性,使得某些轻灵的设计可以在框架层面得以展现。目前的框架技术更多的是在各个层面各自为战,如何将同样的信息从局部传播到整体是一个耐人寻味的问题。
   传统上的程序世界缺乏一些具体的技术手段使得我们可以方便的触及到程序的整体结构部分,这些整体性的关联更多的是存在于文档中,存在于我们的思想中,存在于程序表达世界之外。而AOP技术从本质上说也只是方便在各个层面实现某种局域化的抽象。 当某些东西被拘束在某个具体的孤立的点中的时候,我们似乎就可以松一口气了。但是在程序中仍然存在着大量"弱"的关联,它们很难被清晰的局域化。模型(Model)和Meta必然在程序构建的过程中扮演愈加重要的角色。AOP只是一种技术手段,它必须和更加宽广的框架技术和模型构建技术结合才能起到最大的作用。   

posted @ 2007-01-03 16:10 canonical 阅读(833) | 评论 (1)编辑 收藏

  http://partech.blogdriver.com/partech/1217744.html
  partech基于AspectJ对于AOP的深入应用作了一些有益的探索。ORM的价值之一正在于通过ORM引擎对于对象上的局部操作作出持久化诠释(参见 面向对象之形式系统 )。在partech的方案中,对象删除操作(destroy)的引入显得有些勉强:因为我们需要标记一个删除的时刻,所以调用了一次空的destroy()方法。但是如果在事件驱动的应用场景中,调用时刻唾手可得,这样的问题便很少出现了。
  我个人所关心的方向主要是框架层面上对于AOP概念的应用。在Witrix平台的BizFlow方案中,借助于框架技术的支撑,我们甚至连new和set 调用都不需要,例如在biz文件中只需要声明相应的事件响应函数,框架负责生成界面从用户处收集信息,负责创建对象,负责执行保存和删除操作:no new ,no set, no save, no load, no remove.
java 代码
  <action id="Add-default">
    <source>
       do anything on entity to be added
    </source>
  </action>
  <action id="Remove-default">
  </action>
  <action id="ViewDetail-default">
    <source>
      entity is accessible here
    </source>
  </action>

BizFlow的实作中是实现为DaoWebAction的一个interceptor。

posted @ 2006-12-05 00:40 canonical 阅读(1363) | 评论 (0)编辑 收藏

    面向对象技术中最重要的概念是什么?在面向对象理论发展的初期,几乎所有的正统声音都在鼓吹继承(inheritance)概念,言必称虚拟函数和多态性。但是依赖继承这种推导关系来构建庞大系统的弊病在实践中逐渐暴露出来,随着组件(Component)技术的发展,所谓的封装概念逐渐被推崇为面向对象思想的精华。在此过程中,接口(interface)概念作为系统细粒度正交分解的手段也逐渐发展起来。在软件系统结构的日益复杂化的今天,封装概念开始成为了质疑的对象。是否与一个概念相关的所有实现都要统一封装到一个具体的对象中?伴随着Java,C#等语言进入主流程序界的AOP(Aspect Oriented Programming)给出了不同的答案。在AOP的环境中,Object不再是牢不可破的黑箱模型,而是成为了外部嵌入的方面(Aspect)的容器。在应用AOP这种大范围结构操纵技术的对象构成体系中,封装不再是问题的核心,我们所关注的是面向对象技术中更为本原的概念:this指针。
    在纯粹的技术层面上,面向对象所指的首先是一组相关性的聚集:它指代了一组相关的数据和函数。为了配合这种相关性的表达,在调用形式上发生了重大变化。从全局函数的调用方式转变到了基于对象指针的调用方式:
    func(this) ==> this.func();
    这里关键性的区别在于从全局性的,绝对的表达方式转变为局域化的,相对的表达方式。this指针限定了一个知识域(domain),调用对象函数是在限定知识域的情况下提供一些相对信息,即调用的时候只需要相对知识。例如现在界面上有两个按钮,其中一个跳转到编辑页面,另外一个跳转到列表页面。为了表达出这两个按钮的不同,我们只需要提供非常少的信息。
    〈input value="编辑" onclick="stdPage.gotoEditPage('${pkValue}')" /〉
    〈input value="列表" onclick="stdPage.gotoListPage()" /〉
所有的公共知识集中在stdPage这个对象指针中。所谓组件技术,关键点也正在于这里。基于一个给定的组件对象,我们只需要知道如何调用它的函数,就可以使系统呈现不同的表现形态。我们所关心的并不是如何构造这个知识域(对象本身),而是如何使用相对知识构造出我们最终所需的系统。封装性使我们摆脱了对系统全局知识的依赖。
   从形式主义的角度上说,任何一种调用方式都只是一种表达,它的具体含义需要一个诠释的步骤。基于对象指针的调用形式直接导向了诠释的多样化:我们只需要替换this指针,就可以改变整个一组调用的具体含义。传统上,对象指针是封闭的,指代的是具体的实现,所有的信息都必须来自于对象指针本身,这造成诠释的局限性。但是在AOP的支持下,诠释可以不仅仅是源于其内的,而且可以是发自其外的。例如基于POJO的ORM框架中,我们只需要纯粹的基于对象自身的知识对其进行操作,ORM引擎通过enhance POJO对象来重新将其诠释为对数据库的持久化操作。

posted @ 2006-12-04 00:15 canonical 阅读(1378) | 评论 (2)编辑 收藏

  随着IoC(Inversion of Control)容器的流行,AOP(Apsect Oriented Programming)似乎逐渐成为了主流技术的一部分,但是除了Transaction, Lazy Load, Cache, Log等少量样板应用之外,AOP的技术价值究竟何在? 它能否在广泛的领域发挥作用? 为什么考虑到传统领域之外的应用时,我们的想象力是如此的贫乏?回答这些问题需要对AOP的技术实质作详细的审视.
  传统上, 程序的结构是静态的. 定义了一个类, 它的成员变量和成员函数就是确定的了,定义了一个函数, 它的具体实现也是确定的. 传统程序设计主要定义了一些固化的规则来规范这些确定性组分的组合关系,如类继承体系所表达的推理关系. 而AOP是一种动态代码织入技术, 抽象的说, 一维拓扑的基本元素是线段与边, 而AOP通过mixin, interceptor等机制可以自由的实现这些元素之间的自由组合而不拘泥于预制的规则. AOP就像是一把锋利的砍刀, 我们用它从最终所期望的程序结构中随意的砍下一部分来, 起个名字,就叫Aspect吧. 实际上AOP技术本身并没有限定程序中哪些部分可以作为Aspect, 这种技术本身并不保证你可以抽象得出真正有价值的Aspect, 它只是一种纯粹的程序结构操纵技术而已.
  AOP技术有两个主要组成部分: 定位技术和定位后的组装技术. 定位技术是AOP所宣称的无侵入性的关键所在. 如果我们使用interface等机制来实现功能,则要在程序各处写下调用语句:
    interfaceA.methodA();
    ...
    interfaceA.methodB();
这可以看作是一种占位技术. 定位技术则一般不需要预先在程序中写下什么调用语句, 根据外部的某些定位规则,我们可以在基础的程序结构中搜索到适当的位置. 在理论上说,这种定位方式非常灵活, 即可以是非常精准的定位到某个点,也可以是非常宽泛的定位到一组切入点. 但是, 这里的一个隐含假设是程序基础结构本身已经具备了良好的,具有某种均一性的坐标系, 只有这样我们才能够拥有定位所需的基本信息. 想象一下,如果整个程序只有一个函数, 所有功能的实现通过传入不同的参数值来实现, 则这样的程序结构中是没有什么可定位性而言的. 早期AOP定位所能够依赖的坐标只有类,方法名称, 方法参数类型等, 而这些信息本身又具有自己的业务含义,随着业务的发展,它们本身的名称也可能需要不断的变化,这直接造成AOP所依赖的坐标系的不稳定性.今天还有效的位置描述, 明天也许就突然包括了某些不应该包含进来的程序位置或者排除了某些应在其中的位置. 在JDK5.0中补充的annotation机制为程序补充了新的坐标维度, 基于它无疑可以建立更加灵活而且专用的坐标系统, 它对于AOP的价值必然会逐渐被发掘出来. 在javascript这样的动态语言中,虽然它们内置的动态性直接支持程序结构的动态组装, 但是在定位支持方面却要比java这样的语言弱上很多, 在其上建立AOP应用未见得比java更具优势. 从另外一个角度上说, 定位方式也并不总比占位方式优越. 我们需要牢牢记住"一次描述"的优势在于可以"多次应用". 有的时候我们用很多唇舌去描述一个物品看起来像什么什么样, 有多么大, 多么重, 还不如直接拿给人看, 说:嘿, 就是这个(this).同样在程序中, 占位方式可能更加直接简单,甚至因为代码明确写在那里,概念也更加明确,更加完整. 此外, 在一些特定的程序结构中, 需要定位的位置大大减少, 我们也不需要复杂的定位机制. 例如在witrix的jsplet框架中, 因为它特殊的规范一致性造成程序的处理点只有一个, 应用AOP是一个非常直接的过程.
  AOP的第二个组成部分是组装技术. 程序结构的组装在java中早已不是什么技术难点, 很多人干起这活来都是轻车熟路. 但是组装不仅仅意味着程序结构的融合, 它同时意味着程序运行时状态空间的融合. 在AOP的基础模型中, 在切入点可以得到的变量有this指针,调用函数对象和传入参数列表, 但是AOP本身并没有进一步规范化这些变量的具体形式, 因此在一般情况下, 这些变量对于interceptor来说是只读的, interceptor之间也无法通过这些变量交换信息并协同运行. 这实际上意味着在切点处inteceptor的状态空间是极端受限的. 而当一个切面横跨很多切点的时候, 在interceptor中一般只能对切点处状态空间的共性部分进行操作, 这进一步限制了interceptor的能力.实际上目前AOP的主要应用都是无状态的,或者是基于全局状态空间的(例如transaction interceptor只访问线程上下文), 这反映出对于AOP的primitive方式的应用的一种局限性. 在witrix平台的BizFlow设计中,通过对状态空间明确建模, 实现了基于AOP概念的一种新的应用方式.
  AOP需要对程序坐标空间和状态空间的良好规划, 才能保证无缝的织入, 保证信息交互通道的通畅.应用AOP所指的不仅仅是配置使用现有的AOP实现, 基于这种程序结构操控技术我们所需要做的抽象工作还很多.
 

posted @ 2006-11-19 19:59 canonical 阅读(2728) | 评论 (6)编辑 收藏


  实现是一种具体的东西,对于同一种实现可以有不同层面的各异的诠释,而每一种诠释都可能对应于不同的概念体系。对于一种具体的技术实现,总有人争论说这样这样一下,不是也可以做到什么什么效果吗。但这只是实现上在某种程度上可以达到目标,并不意味着该实现基于的概念体系自身直接体现了我们所需要的结构。这就如同我们可以用汇编实现一切,但并不意味着汇编语言本身的结构包含了所有高级程序结构一样。同样,说到一种失败的技术的时候,也需要区分清楚问题是出在实现细节上还是对应的概念体系上。

posted @ 2006-11-12 14:04 canonical 阅读(361) | 评论 (0)编辑 收藏

  按照Tim Berners-Lee的原始设想,互联网的核心概念是超链接(hyperlink), 每一个可访问的资源都具有自己的URI(Universal Resource Identifier), 我们通过唯一的url可以访问到这些资源。从概念上说,每一个页面可以由一个两元组[title, url]来进行描述,title是url显示在界面上时的可读表述。在这一描述下,我们可以建立基本的页面浏览模型,包括浏览历史模型:我们可以把浏览过的页面保存在浏览历史列表中,通过选择历史列表中的条目实现页面跳转。但是随着网页的动态性不断增加,页面的资源语义逐渐丧失,url所对应的不再是一种静态的资源表述概念,而逐渐演变成为一种动态的访问函数. 例如
  /view.jsp?objectName=MyObj&objectEvent=ViewDetail&id=1
  一个url模式所对应的网页个数在理论上可能是无限多的. 从单一数据值到函数是系统复杂性本质上的一种飞跃. 为了不致在这种无限的世界中迷失方向,我们显然需要对于浏览过程进行更加有效的组织,建立更加有约束性的导航模型. 一种常见的导航模式是在页面的上方显示一条线性的导航路径, 与浏览历史模型不同的是, 页面转换时不是
  list > view item 1 ==>  list > view item 1 > view item 2
而是
   list > view item1 ==> list > view item2
  为了支持导航路径, 最简单的方式是将页面模型扩展为三元组[id, title, urlExpr], 其中urlExpr是动态表达式, 而id是固定的值. 例如 id=view, urlExpr=/view.jsp?objectName=MyObj&objectEvent=ViewDetail&id=${id}
  导航路径的变化规则为navHistory.removeAfter(newPage.id).add(newPage)
  为了进一步约化导航路径, 可以将页面模型扩展为
  [id, title, urlExpr, group, before, beforeGroup],
  其中group定义了页面的分组, 同组的页面在导航路径中相互替代, 而before和beforeGroup定义了页面在导航路径中的相对顺序. 例如对于
  [id='list' beforeGroup="detail"] [id='view' group='detail'] [id='update' group='detail']
在页面转换时, 导航路径变化不是
  list > view item1 ==> list > view item1 > update item1
而是
  list > view item1 ==> list > update item1
  在以上的页面模型中, 每一个id对应的是一个不同的页面模板(页面布局), 但是有时我们也需要在同一个页面布局中浏览无限分级的栏目, 此时可以将页面模型扩展为
  [id, title, urlExpr, group, before, beforeGroup, path]
 
  将以上的导航模型应用于一般的web应用时还需要克服一个小小的困难: 动态url的副作用. 例如/update.do?id=1&value=2这种具有动作语义的url访问直接破坏了页面的浏览模型,例如现在我们不再能够按F5键刷新页面,不能通过window.location=window.location调用来刷新页面,在页面回退时我们也将遇到种种困难。为了防止重复提交,一种常见的设计模式是分离POST和GET方法,即通过Form POST向上提交数据,处理完毕后通过redirect指令通知浏览器再次发起GET方法得到新的页面.具体做法如下  
    /redirect_after_action.do?id=1&value=2
  在执行完action之后, 服务器调用response.sendRedirect(get_url), 通知前台浏览器使用新的url重新取回页面. 这样最终我们可以确保所有页面都是可以通过url直接访问的(没有隐含的post参数),而且是可以重复访问的(无副作用因而可以反复刷新)!
  另一种方式是使用ajax方式提交更新请求,当ajax访问成功后, 在回调函数中进行页面跳转.例如:
  new js.Ajax().setObjectName('Test')
      .setObjectEvent('Update').addForm(myForm)
      .callRemote(function(returnValue){
        window.location = '/view.jsp?id=1';
      })  
这里我们也可以根据returnValue的不同选择跳转到不同页面.
  在Witrix平台中, 基于Jsplet框架我们定义了PageFlow对象, 它将可配置的导航模型引入到任意页面的组织过程中. 在跳转到一个新的页面的时候, 访问url如下:
  /pageflow.jsp?objectName=MyNav&objectEvent=NavToPage&_pageId=view&id=3
在重新访问历史页面的时候,只需要
 /pageflow.jsp?objectName=MyNav&objectEvent=NavToHistoryPage&_pageId=view
  基于jsplet框架的对象化特性,MyNav对象在session中保持了flow的所有状态变量,不需要任何框架上特殊的支持。我们可以在任意页面跳出pageflow, 并在任何时刻选择跳回pageflow, 这些动作不会影响到flow对象的状态。而通过objectScope的设置我们可以精细的控制flow对象的生命周期。同时基于对象化设置,访问页面时我们只需要资源的相对名称(relative identifier), 例如对于各种不同的flow, 页面id都可以叫做view, 通过_pageId=view来访问。
  Apache软件基金会旗下有一个beehive项目, http://beehive.apache.org/docs/1.0m1/pageflow/pageflow_overview.html, 其中也定义了所谓pageflow的概念, 这是始创于BEA的一项技术. 但是与Witrix Page Flow不同的是, Beehive Page Flow的核心概念仍然是从struts引申而出的action的概念,所谓的flow是action的连接,而不是通过资源id之间的连接。虽然beeive宣称支持各种对象injection, 并且管理了flow对象的生命周期,但是它并没有规范出this指针这一面向对象最重要的概念之一。Witrix Page Flow关注的重点是已存在的页面之间的有效组织, 它所描述的是某种具有静态资源语义的页面模板之间的关联, 这种关联是较松散的,而不是通过action强关联的. 实际上基于Ajax技术和jsplet框架的消息路由机制, 在每一个页面上都可以进行多种业务操作,甚至更换页面,而不发生pageId的变动, 并不是所有的操作过程都需要在page flow描述中对外暴露。另外一个比较细节化的不同之处是Beehive使用Java Annotation, 而Witrix PageFlow使用xml描述文件, 并实现了pageflow的继承和部分覆盖等高级构造方法. 使用xml描述文件的必要性在于需要复用WebAction, 支持复杂的变换操作, 并集成tpl模板引擎等. Annotation的方式看似简单,但这种简单性同时将系统中的所有组分绑定到了一起, 它也不允许复杂的变换操作的存在. 实际上Beehive的目标并不是真正支持页面编制(包括页面上的页面操作)和页面组织的分离.

posted @ 2006-11-05 20:35 canonical 阅读(2287) | 评论 (7)编辑 收藏

  IoC(Inversion of Control)是一个很宽泛的概念,对于我们常说的IoC容器(如spring)所做的工作,一个更加准确一些的说法是依赖注入(Dependency Injection), 即容器将一个对象所依赖的其他对象push到该对象中,而不是该对象从外界环境pull相关的依赖对象. 不过, 细究起来这种依赖注入仍然只是DI的一种特殊形式, 可以将它称之为data dependency injection, 因为IoC容器所许诺的是: "啊哈, 当我们需要对象A的时候,它就在这儿". 虽然IoC容器管理的不仅仅是数据,而是具有行为的对象,但是如果要让这些行为具体发生, 我们仍然需要额外的调用步骤.
  对于一个自动触发behaviour的系统, 我们一般将之称之为引擎(Engine). 例如工作流引擎在处理完本步骤的业务逻辑之后会自动触发流程流转操作. 一个引擎对于我们的承诺是: "当我们需要某个behaviour的时候, OK, 它会在适当的时候发生的". 对于一个软件开发框架或者更宏大的软件开发平台而言, 如果我们以业务逻辑为主体来审视整个程序运行过程, 则它们的核心价值也在于在适当的时候将适当的操作Inject到业务逻辑中. 对于目前所谓的软件开发平台而言, 除了工作流的内容之外, 一个主要部分就是CRUD(Create/Read/Update/Delete) Ready. 一个开发平台的优劣往往直接体现在它在多大程度上能够将CRUD操作剥离出主体程序逻辑, 这不仅仅涉及到数据库存取操作, 同时还包含界面交互, 数据逻辑关联等.
  除了引擎,框架,平台等应用层面的实现之外, AOP(Aspect Oriented Programming)在语言层面为behaviour注入也提供了一种特定的实施手段. 在AOP的概念中, 往往作为切点的函数被认为是基础的部分, 而interceptor是在基础蓝图上的一种扩展. 这也体现在基础函数定义了当时执行环境中可以访问的状态变量(参数/属性),而interceptor则依附于pointcut处所能得到的状态变量, 它本身一般并不维护独立的状态变量(不产生也不消灭状态变量). 从数学上看, base function和interceptor之间构成一种对偶(dual)关系, 当我们的关注重点转移到interceptor上来之后, 它本身也应该具有完整的业务语义, 这需要对AOP的执行过程做小小的偏置处理.
  在witrix平台的BizFlow设计中,BizFlow是通过类似AOP的方法作为CRUD操作的Filter实现的, 但是从bizflow本身的配置文件来看,它可以构成一个完整的业务描述, 而CRUD成为某种自动注入的behaviour.例如对于新增操作, BizFlow中的配置可以如下
  <action id="Add-default">
  </action>
虽然没有写任何代码, 新增操作(包括从request中读取操作并做有效性校验等操作)是自动进行的. 我们也可以在新增前和新增后分别执行一些操作
  <action id="Add-default">
    <source>
      some operation before add
      <biz:Proceed/>
      some operation after add
    </source>
  </action>
与AOP中的常见做法不同, 这里并没有明确定义新增前和新增后这样的切点, 而只是定义了Add-default这样一个action. 在BizAction的source段可以执行任何tpl代码, 而tpl代码的执行上下文可以看作一个Map, tpl代码执行过程中可以获取变量, 也可以设置任意变量, 因而bizFlow拥有对于状态空间的完全的控制权.

posted @ 2006-10-22 18:44 canonical 阅读(1253) | 评论 (0)编辑 收藏

  对于目前MDA(Model Driven Architecture)的理论和实现,我一直持一种消极态度。以前和hotman_x的讨论中,我也明确表述过对于MDA的看法。
  MDA:以有限搏无限 http://canonical.blogdriver.com/canonical/787637.html
  图形 vs. 文本 http://canonical.blogdriver.com/canonical/1090209.html
  所谓的MDA一般总是从高层抽象模型出发,希望通过预定的建模过程推导出底层的全部实现细节。但是implementation is also interpretation. 实现过程本身也是对高层模型的一种诠释过程, 是一个逐步明晰并逐渐消除概念之间矛盾冲突的过程。从高层模型到底层实现并不是一个同构(isomorphism)的过程,甚至一般情况下也不是同态(homomorphism)的。在概念模型到具体代码实现的过程中,总是存在着需要不断补充的细节。这些细节如何才能成为高层模型的一种自然的衍生部分是一个非常复杂的问题。如果考虑得太细(需要指定过多难以从整体上进行控制的参数),似乎就会丧失高层模型的抽象性和概括性,而如果不深入到细节,则难以平衡高层模型之间的互相冲突的属性。随着细节的不断增加,试图维持高层模型在各个层面的统一性无疑将变得异常困难。实际上在每一个抽象层面概念都可能出现重组和混合的情况,试图统一建模在目前的技术水平下是不太现实的。
  MDA所需要解决的一个核心问题是维护模型的持续有效性, 即当根据模型构造出实际系统之后, 对模型的修改仍可以自动反映到已实现的系统中, 而不是每次重新生成一个新的系统. 或者说MDA应当如何支持实现层面的重构. 为了解决这个问题, 一般的实现策略是建立完整的程序模型, 提供一个强大的集成开发工具, 可以在一个特意构造出的IDE环境中对模型进行调试, 修正, 尽量避免程序员直接接触实现代码, 确保一切细节尽在单一开发工具(单一信息驱动源)的掌握之中. 但是很显然, 这样一个大一统的开发工具在各个层面(如数据库设计, 表单设计等)都只能是专业工具的一个简化版. too simple, sometimes naive. 当我们需要对程序有较深入的控制力的时候, 这些工具往往就很难起什么作用了, 甚至会成为某种障碍.
  在witrix平台的设计中, 也有部分"MDA"的内容. 只是我们的设计思想是Physical Model Driven(物理模型驱动), 而不是Logical Model Driven(逻辑模型驱动). 具体做法是
1. 采用power designer建立数据库物理模型(PDM 而不是 CDM), 然后根据一些命名约定和附加注释(例如pdm中的package映射为java实体类的package, pdm的domain指定字段是否附件字段等)来标注出物理元素的逻辑含义.
2. 解析pdm文件, 生成hibernate映射文件(.hbm.xml), meta文件(.meta.xml), spring注册文件(.action.xml)等
3. 通过jboss的hibernate-tools工具生成java实体类.
   
  根据自动生成的配置文件, 可以直接完成对于数据库的增删改查操作, 包括维护一对多,多对多等关联关系. 此后我们可以根据程序具体需求, 对生成的文件进行修改. 通过一些程序设计技巧, 我们可以实现手工修改的代码与工具自动生成的代码之间始终有明确的边界, 从而可以做到pdm与代码的自动同步, 不需要手工进行任何调整.
  我们选择从PDM出发的一个基本理由在于, 从高层模型向下, 路径是不确定的,而从物理模型向上,路径是确定的. 在pdm中我们需要做的不是补充细节(增加新息)而是标注出已经存在的逻辑概念。选择PDM建模也集中体现了我所一直倡导的Partial Model的概念. PDM模型并不包含界面的具体展现方式, 也不包含更加复杂的业务处理过程, 它只是完整程序模型的一部分. 我们不试图建立一种完全的模型,追求概念上的一种自我完备性, 而只是关注当某些被共享的模型元素发生变化的时候, 这些变化如何以保真的方式传播到系统各个角落. 实际上一旦获得物理实现,高层模型在某种意义上就变得不再那么重要了. 为了支持模型的局部修改, 我们只需要从物理模型中提取部分信息,而不需要恢复出一个完整的业务模型。我们不需要把一个高层概念在各个层面的表达都考虑清楚,我们只需要知道某一特定的物理模型应该对应的部分高层模型即可。
  目前国内一些软件开发平台也包含所谓MDA的部分, 例如浪潮Loushang MDA (www.loushang.com)号称不需要写一行代码,定制出所需应用系统. 可从其技术白皮书来看, 它所谓的Model虽然是使用UML来建立, 但是大家似乎故意忘记了对象是成员变量+行为构成,不包含行为模型的对象模型不过是数据模型的一种翻版而已. 从Loushang MDA的元模型对象的UML图可以看出, MofTab, MofReference等固定了几种界面显示模式, 似乎其MDA只是针对既定场景应用的一种预制代码框架. 从我们的实践来说, 数据模型驱动的应用并不需要限制在基础数据对象维护这一非常特定的领域,而可以在通用应用领域发挥作用。

posted @ 2006-09-10 22:19 canonical 阅读(1571) | 评论 (4)编辑 收藏

仅列出标题
共18页: First 上一页 3 4 5 6 7 8 9 10 11 下一页 Last