posts - 175, comments - 240, trackbacks - 0, articles - 6

    分层是最常见的软件架构方式之一。分层之后可以区分出横纵两个维度,纵向往往表现出一种隔离性。出于有意无意的各种原因,层次之间传递信息很容易出现模糊甚至丢失的现象。B/S多层体系架构下的程序因为浏览器和服务器之间的状态空间相互独立,相对于共享全局状态空间的C/S程序,更容易出现信息传递不畅的问题。实际上,我们经常可以观察到B/S程序中存在着大量的"接力"代码,即在交界处,总是存在着大量用于读取变量,拼接变量,转换变量等与主体业务无关但却又不可或缺的代码。在多层架构程序中,信道构建应该是一个需要给予足够重视的问题。

    在系统规划中,多层结构应该内置与具体语义无关的通用信道,它跨越多个层次,允许信息透明的通过,并以未预期的方式在不同的层面激发各种相关的行为。在Witrix平台中,平台代码与特定应用中的业务代码处于高度交织的状态,一个特定业务功能的实现往往需要多处业务代码相互协同,平台必须成为某种透明的背景。例如,假设我们编制了一个通用的列表选择控件,它封装的逻辑是从一个实体列表中进行选择
      <app:SelectOne objectName="MyEntity" />
如果现在要求选择时只列出某个类型的实体,则调用形式为
      <app:SelectOne objectName="MyEntity" extArgs="$bizId=select&amp;$type=1" />
在调用入口处补充必要的信息之后会推动系统在遥远的状态空间中应用一个特定的过滤条件。这里$bizId负责指示平台应用特定的元数据配置,而其他的参数则由元数据中的逻辑负责处理。平台与特定业务代码各取所需,相互配合,将尽可能多的逻辑剥离为通用机制。


posted @ 2009-03-22 21:10 canonical 阅读(442) | 评论 (0)编辑 收藏

javaeye提供的电子书制作功能非常实用。
http://www.blogjava.net/Files/canonical/canonical-blog-20090228.rar

posted @ 2009-02-28 17:21 canonical 阅读(258) | 评论 (0)编辑 收藏

   现代数学是建立在等价类这一概念的基础之上的。同构是对等价关系的一种刻划。简单的可以把它理解为两个系统之间的一种“保持”运算规则的一一对应关系。在 数学中一个符号所代表的是所有能够互相同构的对象。例如整数3可以看作是与某个元素个数为3的集合可以建立一一对应关系的所有的集合所构成的整体。所以在 数学中,如果我们解决某个特定的问题,它同时也就意味着我们解决了一系列相互等价的问题。
    同构关系对于认知可以起到本质上的简化作用。如果通过一个推理链条,确认了A == B == C == D,则可以直接从概念上推导出 A == D, 这一关系有可能被直观理解,而不需要理会中间的推理步骤。(注意到以上元素两两建立同构关系的时候可能要采用不同的对应手段,因此上面的等式并不是平凡 的。)另一方面,我们可以确定一个模型元素M,  将系统简化为 A == M, B == M, C == M, D == M。只要理解了元素M就理解了等价的其他元素。

    Witrix平台中PDM定义作为基础的结构模型,它同时映射成为数据库表,以及hbm, java, meta等多个代码文件,此外还对应于约定的WebObject名称和BizFlow文件名称,相应的报表文件目录等。我们只要理解了pdm模型,即可通 过推理自然的掌握各个层面上对应的结构。这意味着只要知道实体名称,就知道如何通过Web访问这个对象,知道数据在数据库中对应的数据库表,而不需要知道 后台是如何读取前台提交的参数以及如何执行保存数据指令的。不仅仅是在模型驱动领域,在系统设计的各个方面,我们都应该尽量充分的利用当前的信息通过推理 得到系统其他部分的结构,而不是通过手工关联或者判断在程序中动态维持这种对应关系。例如在flow-cp机制中,biz的id, action的id等都根据step配置的id推导得到,这样在工作列表跳转的时候就可以根据规则推导出跳转页面对应的链接,而不需要手工编写页面重定向 代码。




 
    同态(homomorphism)关系相对于同构关系,只强调单向映射的可行性,它是一个舍弃属性的过程。同态作为最基础的认知手段之一,它不仅仅是用一 个符号来置换一组元素,而是同时保留了某种全局的运算关系,因此同态映像可以构成某种独立的完整的研究对象。通过同态映射,我们可以在不同的抽象层面上研 究原系统的一个简化版本。例如meta中的layout是一种典型的领域特定语言(DSL)。
    userName userTitle
    emailAddress

每一个字段表示了一个可能任意复杂的inputor或者viewer, 字段之间的前后关系描述了最终显示页面上显示内容的相对关系。当viewer根据需求发生改变的时候,并不影响到layout层面上的关系,因此 layout可以保持不变。如果我们在系统中把问题分解为多个抽象层面上,多个观察视角上的同态模型,则可能实现更高的软件复用程度。
    在Witrix平台的设计中,很多细粒度的标签都定义为tpl文本段,这样平台只要理解某一层面上的交互关系,实际应用中可能出现的细节在标签内部进行局 部处理,不会突破原始设计的形式边界,不会影响到原先设定的主体系统结构。例如BizFlow中的tpls段,action的source段等。
    上世纪50年代以前,生物学家做梦也想象不到具有无限复杂性的生物遗传过程,竟然可以被抽象为ATGC四个抽象符号的串联。生命竟然不理会各种已知的或是 未知的物理化学作用,被抽象的三联码所驱动。一种抽象的本质似乎成了生命世界的本原。在软件的世界中,可以被识别的抽象元素绝不只是语言本身所提供的那些 机制。

posted @ 2009-02-28 16:57 canonical 阅读(1443) | 评论 (0)编辑 收藏

      有一个心理学实验,要求被试者将青草,公鸡,牛三个东西分 成两组,结果多数中国儿童将青草和牛分成一组,而多数美国儿童将公鸡和牛分成一组。中国人的思想中青草和牛之间存在现实的关系,牛吃草,而西方人的典型逻 辑是公鸡和牛都属于动物这一范畴。通过分类将物体类型化,这是西方人从小就接受的训练。据说美国婴儿学习名词的速度要快于动词,而中国的婴儿则相反,这并不是偶然的。

       中国人的传统哲学认为世界是普遍联系的,事物之间存在着祸福相依的辩证转化关系。而古希腊人强调个体意识,以两分法看待世界,他们将世界看成是孤立的物体组成(原子论)构成,然后选择一个孤立物体(脱离背景),开始研究它的各项属性,接着将属性泛化,构成分类的基础。西方语言中大量抽象概念都是从作为属性的形容词直接转化而来,例如 white --> whiteness 。而中文中很少有精确的类型定义,而多半是富有表现力的,隐喻性的词语,例如我们不谈论抽象的白,而只说雪白,没有抽象的 size ,而只说具体的大小。

       亚里士多德认为铁球在空气中下落是因为它具有“重性”,而木块在水中漂浮是因为木块具有“轻性”。这种将一切原因归结为事物内在属性的传统在一定程度上妨碍了西方人认识到背景的存在和作用,但使得他们可以把问题简化。

       古希腊人对于类型的热衷源于他们对于永恒的迷恋。静态的亘古不变的世界才是他们的思想栖息的场所。具体的物体是易逝的,多变的,只有抽象的类型才是永恒的存在,也只有抽象概念之间的关系才是永真的联系。而具体实例之间的关联在某种程度上被认为是不重要的,甚至是不可靠的。



       将具有某一属性的所有物体定义为一个集合,这一做法在上世纪初被发现会引起逻辑悖论,动摇了整个数学的基础,它绝不像表面上看起来那么单纯。但确定无疑的是,通过类型来把握不变的事实是一种非常重要且有效的认识策略。面向对象语言强调名词概 念,从引入类定义以及类之间的继承关系开始,这符合西方一贯的作风。而 Ruby 这种强调实例间关系的动态语言首先由日本人发明,可能也不是偶然的。虽然现在大家都在玩高科技了,可实际贩卖给你的多半仍然是包治百病的祖传秘方。文化可能造成认知上的一种偏执,在技术领域这一现象并没有被清楚的意识到。

 

posted @ 2009-02-21 19:43 canonical 阅读(1600) | 评论 (2)编辑 收藏

    软件开发作为一种工程技术,它所研究的一个重点就是如何才能有效降低软件产品的研发成本。在这一方向上,组件技术取得了空前的成功。它所提供的基本图景是 像搭积木一样从无到有的组装出最终的产品。在某种意义上,这是对现代建筑工业的模仿和致敬。新材料,预制件,框架结构,这些建筑学的进展在软件领域被一一 复制,建筑工地上的民工自然也成为了程序员们学习的楷模。毕竟,在组件的世界中码代码,基本上也是一种“搬砖”的行为。

    值得庆幸的是,软件开发作为一种智力活动,它天生具有一种“去民工化”的倾向。信息产品有着与物质世界产品完全不同的抽象本质。在物理空间中,建造100 栋房屋,必须付出100倍的努力,老老实实的干上100遍。而在概念空间中建造100栋房屋,我们却可以说其他房屋与第一栋一模一样,加点平移旋转变换即 可。一块砖填了地基就不能用来盖屋顶,而一段写好的代码却可以在任何可用的场合无损耗的被使用。一栋建好的房屋发现水管漏水要大动干戈,而在完成的软件中 补个局部bug却是小菜一碟。在抽象的世界中有效的进行生产,所依赖的不应该是大干,苦干的堆砌,而应该是发现某种可用于推导的原理,基于这些原理,输入 信息可以立刻转换为最终的结果,而不需要一个逐步构造的过程。即我们有可能超越组装性生产,实现某种类似于数学的原理性生产。http://canonical.javaeye.com/blog/325051

    代码复用是目前软件业中鼓吹降低生产成本的主要口号之一。但是在组件技术的指向下,一般所复用的只是用于构建的砖块,而并不是某种构造原理。即使在所有信 息已经确定的情况下,我们仍然不可能从需求立刻得到可执行的产品。很多代码即使我们在想象中已经历历在目,却仍然需要一行行把它们誊写下来。当我们发现系 统中已经没有任何组件值得抽象的时候,仍然留下来众多的工作需要机械化的执行。代码复用的理想国距离我们仍然非常的遥远。

    子例程(subroutine)是最早的代码重用机制。这就像是将昨天已经完成的工作录制下来,在需要的时候重新播放。函数(function)相对于子 例程更加强大。很多代码看起来并不一样,但是如果把其中的差异部分看作是变量,那么它们的结构就可以统一了。再加上一些判断和循环,很多面目迥异的东西其 实是存在着大量共享信息的。面向对象技术是一次飞跃性的发展。众多相关信息被打包到一个名称(类型)中,复用的粒度和复杂度都大大提升。派生类从基类继 承,可以通过重载实现对原有代码的细致调整。不过,这种方式仍然无法满足日益增长的复用需求。很多时候,一个名称并不足以标定我们最终需要的代码结构,在 实际使用的时候还需要补充更多的信息。类型参数化,即泛型技术,从事后的角度看其实是一种明显的解决方案。根据参数动态的生成基类自然可以吸纳更多的变 化。经历了所谓Modern C++的发展之后,我们现在已经非常明确,泛型并非仅仅能够实现类型共变,而是可以从类型参数中引入更丰富的结构信息,它的本质是一种代码生成的过程。http://canonical.javaeye.com/blog/57244 认清了这一点,它的扩展就非常明显了

   BaseClass<ArgClass> --> CodeGenerator<DSL>

DSL(或者某种模型对象)相对于普通的类型(Class),信息密度要大很多,它可以提供更丰富也更完整的输入信息。而CodeGenerator也不必拘泥于基础语言本身提供的各种编译机制,而是可以灵活应用各种文本生成技术。http://canonical.javaeye.com/blog/309395 CodeGenerator在这里所提供的正是根据输入模型推导出完整实现代码的构造原理。

    现在很多人热衷于开发自己的简易代码生成工具,这些工具也许可以在简单的情形下减轻一些体力工作,但是生成的代码一般不能直接满足需求,仍然需要手工执行 大量的删改工作。当代码生成之后,它成为一种固化的物质产品,不再能够随着代码生成工具的改进而同步改进,在长期的系统演化过程中,这些工具并不一定能够 减少累积的工作量。

   修正过程  ==> CodeGenerator<DSL>

为了改进以上代码生产过程,一些人试图在CodeGenerator中引入越来越多的可配置性,将各种变化的可能内置在构造原理中。显然这会造成CodeGenerator的不正常的肿胀。当更多的偶然性被包含在原理中的时候,必然会破坏原理的简单性和普适性。

   输入信息 + 一段用于推导的原理 + 修正补充 = 真实模型

必须存在[修正补充]这一项才能维持以上方程的持久平衡。

    为了摆脱人工修正过程,将模型调整纳入到概念世界中,我们需要超越继承机制的,更加强大的,新的技术手段。其实在当前的技术背景下,这一技术已经是呼之欲出了。这就是AOP, Aspect Oriented Programming。http://canonical.javaeye.com/blog/34941

      Biz ==[AOP extends]==> CodeGenerator<DSL>

继承仅仅能够实现同名方法之间的简单覆盖,而AOP所代表的技术原理却是在代码结构空间中进行任意复杂的删改操作,它潜在的能力等价于人工调整。

    为了实现上述生产模式,需要对编程语言,组件模型,框架设计等方面进行一系列改造。目前通用的AOP实现和元编程技术其实并不足以支持以上模式。http://canonical.javaeye.com/blog/275015
    这一生产模式将会如何演化,也是一个有趣的问题。按照级列理论,我们立刻可以得到如下发展方向:

    Context0 + DSL1 + EXT0 = DSL0  
    Context1 
+ DSL2 + EXT1 = DSL1 
   

 http://canonical.javaeye.com/blog/33824

Witrix平台中BizFlow可以看作是对DaoWebAction的修正模型,但是它本身具有完整的意义,可以直观的被理解。在BizFlow的基础上可以逐步建立SeqFlow,StateFlow等模型。http://canonical.javaeye.com/blog/126467

      现在有些人试图深挖函数式语言,利用模式匹配之类的概念,做符号空间的全局优化。但是我们需要认识到通用的机制是很少的,能够在通用语言背景下被明确提出 的问题更是很少的。只有在特定领域中,在掌握更多局部信息的情况下,我们才能提出丰富的问题,并作出一定背景下的解答。DSL的世界中待做的和可做的工作 很多。http://canonical.javaeye.com/blog/147065

      对于程序员而言,未来将变得越来越丰富而复杂,它将持续拷问我们的洞察力。我们不是一行行的编写代码,把需求一条条的翻译到某种实现上,而是不断发明局部的生产原理,依靠自己制定的规则在抽象的空间中不断的创造新的表象。

posted @ 2009-02-15 18:21 canonical 阅读(1854) | 评论 (2)编辑 收藏

       负数没有直接的几何意义,因此它被认为是对应于不存在的事物。而按照古希腊的逻辑,不存在的事物是不可能存在的,因而也就是无法被理解的,更不可能参与到 推理过程中,因此是无意义的,无法被定义的, 因此它是不存在的。中国人注重的是运算的合理性,而不是数的真理性,大概在公元前400年左右就创造了负数和零的概念。而在西方直到公元7世纪(唐代)的 一本印度人的著作中才出现负数,它被用来表示负债。西方人没有能够创造负数,他们对负数的接受更迟至15世纪左右。这件事实在一定程度上说明了存在某种深 刻的困难阻碍我们理解负数概念。
       在引入负数之前,3x^2 + 8 = 4x 和  3x^2 + 4x + 8 = 0 这两个方程的结构是完全不同的,它们需要不同的求解技术,因此也就不可能利用符号抽象出 a x^2 + b x + c = 0。引入负数才使得我们能够以统一的方式提出问题,并研究通用的求解技术。
      群论(Group Theory)是对结构进行抽象研究的数学分支。群的定义包括四条规则
1.    元素之间的运算满足结合律 (a * b) * c = a * (b * c)
2.    元素之间的运算封闭,即 a * b 仍然属于该群
3.    存在单位元,即对所有a, a * e = e*a = a
4.    每个元素存在对应的逆元,a * a^-1= e
      逆运算是非常重要的结构要求,逆元是对负数的一种抽象推广。如果没有逆元,则只能构成半群(semi-group),它的性质要少很多。


      目前软件设计中所有的原则都指向组装过程,从无到有,层层累进。构件组装的隐喻中所包含的图像是操纵实际可见的积木,是缺少逆元概念的。

      考察一个简单的例子,假设需要的产品是三角形内部挖去一个五边形的剩余部分。有三种生产策略:
1.    对最终需要的产品形态进行三角剖分,使用8个小三角形拼接出来。这种方式比较繁琐,而且最后粘接工序的可靠性和精确性值得怀疑。
2.    拿到一个真实的三角形,然后用刀在内部挖出一个五边形的洞。这种方式需要消耗一定的人工,而且也很难保证五边形的精确性,即使我们曾经精确的生产过其他五角形和三角形。实际上一般情况下我们是逐步锉出一个五边形的,并没有充分利用到五边形的对称性。
3.    在概念空间中做一个抽象计算  (-五边形) + (三角形) = 所需产品
如果我们能够生产一种负的五边形和一种正的三角形,则可以立刻得到最终的产品。



 
在软件开发的实践中,我们目前多数采用的是两种方式:
1.    采用可视化设计工具通过拖拽操作开发出完整的界面和后台
2.    拷贝一些已有的代码,删除掉不需要的部分,增加一些新的实现,也可能对已有实现做一些不兼容的修正。

在第二种方式中
           新结构的构造 = 已有结构 + 软件之外的由人执行的一个剪裁过程
这个剪裁过程表现为一个时间序列。如果我们对原有结构进行了调整,则需要重新关联一个时间序列,而此时间序列并不会自动重播。为了压缩以时间为度量单位的 生产成本,我们必须减少对时间序列的依赖。在时间序列中展开的一个构造过程可以被转化为一个高维设计空间中的一种更加丰富的构造原理,我们最终的观测可以 看作是设计空间向物理空间的一个投影(想象一束光打下来)。这种方式更容易保证程序的正确性。
          时间序列 --[原理转化]--> 空间关系


    这样我们就可以使用第三种生产策略:利用构造原理进行抽象计算。如果我们只盯着产品的最终形态看,只是想着怎么把它像搭积木一样搭建出来,就不可能识别出 系统结构本身所蕴含的对称性。如果我们发现了系统内蕴的结构特征,但是却只能通过构造过程中的行动序列来追随它,同样无法实现有效的工作复用。同时因为这 个行动序列一般处于系统规则约束之外,完全由人的自觉来保障,因此很难保证它的正确性。现实世界的规范要求并不是模型本身所必须满足的,只要我们能够创造 新的结构原理,在概念空间中我们就可以拥有更多的自由。现在业内鼓吹的软件构造原理多半是参照物理世界中生产具体物质产品的生产工序,却没有真正把握信息的抽象本质。掌握规则,制订规则,才是信息空间中的游戏规则。

    物理学中最重要的分析学思想之一是微扰论(Perturbation). 针对一个复杂的物理现象,首先建立一个全局的规范的模型,然后考虑各种微扰条件对原有模型的影响。在小扰动情况下,模型的变化部分往往可以被线性化,被局 域化,因而问题得到简化。微扰分析得到的解依赖于全局模型的解而存在,因而这是一种主从关系的分解方式。但是如果主体模型是我们已经熟知的物理现象,则我 们关注的重点可以全部放在扰动解上,认为所有特定的物理规律都体现在扰动解中。如果微扰分析得到的物理元素足够丰富,则微扰模型本身可以成为独立的研究对 象,在其中我们同样可以发现某种普适的结构规律。
    考察如下的构造过程
       X = a + b + c
       Y = a + b + d = (a + b + c) - c + d = X - c + d
    对于数学而言,上述的推导是等价的,但是对于物理学而言,Y = a + b + d 和  Y = X - c + d是有着本质不同的。第一种方式要求打破原先X的构造,而重新的组装其实是有成本的,特别是在X本身非常复杂的情况下。典型的,如果X是经过测试的功能, 重新组装后原先由测试保障的概念边界被打破。
         我们可以从Y = X + dX抽象出扰动模型  dX = - c + d
主从分解模式自然的导出逆元概念。

      如果没有逆元,我们必然需要分解。但是如果发掘了背景这一概念,在逆元运算下,对背景不是分解让其成为可见的部分,而是采用追加的,增删的方法对背景结构 进行修正,则我们有可能在没有完整背景知识的情况下,独立的理解局部变化的结构。即背景是透明的,知识成为局部的。在Witrix平台中,BizFlow + DaoWebAction + StdPage 才构成完整的程序模型,BizFlow其实是对标准模型的差异描述,但是它可以被单独的理解。如果我们从接触程序开始就接受BizFlow, 就可能完全不需要了解数据库访问和前台界面渲染的知识。我们并不是通过在DaoWebAction中设定各种可预见的调用形式,而是在BizFlow中通 过类似AOP的操作方式直接对标准模型进行修正。这种修正中一个很重要的部分就是删除标准模型中缺省提供的功能。
     WebMVC之前世今生 http://canonical.javaeye.com/blog/163196
     Witrix架构分析 http://canonical.javaeye.com/blog/126467


      变化的部分构成独立于原始模型的新的模型,它的结构关系是完备的,可以独立的理解。在原始模型崩溃的情况下,它仍然可能保持有效性。
      从物理学的角度看,我们所观测到的一切物理现象,都是某种物理作用的结果,也就是物质结构相对于背景状况的一种偏离。我们只可能观测到变化的部分,因此我们对世界的认识其实只是世界的扰动模型而已,世界的本体不属于科学研究的范畴。

posted @ 2009-02-07 22:22 canonical 阅读(1423) | 评论 (1)编辑 收藏

    软件技术的发展是一个结构化不断加深的过程,我们逐渐拥有了越来越丰富的结构识别, 表达和处理手段。在这一方向上, 组件技术最终取得了巨大的商业成功。但是区分同时也就意味着隔阂。面向对象技术最基础的概念在于 O = C(O), 对象的复合(Composition)仍然是对象.  然而在越来越复杂的软件生态环境中,这一图景实现的可能性被大大的压缩. 面对层层封装造成的形态各异的表达方式, 不同来源, 不同目标, 不同运行环境下的信息的交互变得越来越困难。我们逐渐丧失了概念上的简洁性, 也丧失了数学世界中的那种穿透一切的统一的力量. 所谓SOA(Serivce Oriented Architecture)技术试图通过补充更多的环境信息,放弃状态关联,暴露元知识等方式来突破现有的困境。 http://canonical.javaeye.com/blog/33803 这其中一项关键的技术抉择是基于文本格式进行信息表达。

      在过去10年中Web技术取得了空前的成功,它造就了互联网这一世界上最大的分布式集成应用。SOA从某种程度上说是对这一事实在技术层面上的反思。基于 文本传递的web技术所表现出来的开放性,可理解性和可观测性,与封闭的,难以直接解读的,必须拥有大量相关知识才能正确操作的二进制结构相比,这本身就 是革命性的创新。不需要特殊的工具就可以非常轻易的察看到网页程序的输入输出,所有交互过程都处在完全透明的检查下,各种不曾事先规划的文本处理手段都可 以参与到web创建的过程中。随着速度,存储不再是我们考虑的首要问题,文本化作为一种技术趋势逐渐变得明确起来。但是任何一种技术思想的成功或失败都不 可能是因为某种单一的原因所造成的,因此有必要对文本化的技术价值做更加细致的剖析。
  
   1. 文本化是一定程度上的去结构化,但是通过这种方式我们获得了对程序结构的更强的控制力。无论我们所关心的文本片断层层嵌套在大段文本的哪个部分,我们都可 以通过text.indexOf(subStr)来实现定位,通过text.replace(oldStr,newStr)实现变换。而在组件系统中,我 们只有通过某种预定的遍历方式逐步递归才有可能访问到组件内部的组成对象。如果某个组件不按照规范实现或者规范中缺少明确的设定,则这一信息关联链条将会 断裂。在一些组件系统中,甚至没有规定一种统一的遍历方式,则我们根本无法定义出通用的大范围的结构定位/变换手段。即使是在设计精良的组件框架中,受限 制于组件的封装性要求,我们也不可能访问到全部的信息。当我们以组件设计者意料之外的方式使用这些组件的时候,就会遇到重重障碍。即使所需的只是微小的局 部调整,因为无法触及到需要修改的部分,我们也可能被迫放弃整个组件。
  
   2. 文本是普适的信道。各种操作系统,各种程序语言所具有的最基本的能力就是文本处理能力,对于文本格式的约定是最容易达成共识的。虽然对于机器而言,理解基 于地址定位的二进制数字可能更加直接,但是所有的应用程序都是由人来负责编制,调试,部署,维护的,如果人可以不借助特殊的工具就可以明确了解到系统内发 生的过程,则系统的构建和维护成本就会大幅下降。
  
   3. 文本表述形式上的冗余性增强了系统的概念稳定性和局部可理解性。在二进制格式中,经常出现的是根据相对地址定位,这要求我们完整理解整个二进制结构,才能 够逐步定位到局部数据块。同时,二进制格式中也经常使用一些外部的信息,例如某个位置处的数据为整型,占用四个字节等。这样的信息可能只能在文档说明里查 到,而在数据体中没有任何的体现,这限制了独立的理解可能性。与此相反,文本格式经常是自说明式的,例如width:3.5px, 这提高了系统的可理解性,特别是局部可理解性。即使我们对系统只具有很少的知识,一般也能根据数据周围的相关信息进行局部操作。一般很容易就能够定位到特 殊的局部数据区,安全的跳过众多未知的或者不被关心的结构. 一个程序新手也可以在很短时间内靠着连蒙带猜, 实现xml格式的word文件中的书签替换功能,而要搞清楚word的二进制格式,并独立编制出正确的替换功能,显然就不是一两周的工作量可以解决的了. 这其中, 可理解性的差异是存在着数量级上的鸿沟的.
  
   4. xml这种半结构化的文本格式规范的兴起, 以通用的方式为文本描述引入了基本的形式约束, 实现了结构表达的均一性. C语言中的宏(Macro)本质上就是一种文本替换技术,它的威力在于没有预定义的语义, 因此可以超越其他语法成分, 破除现有语法无法跨越的限制. 但是它的危险性在于缺乏与其能力相适应的形式约束, 难以控制. 而在xml格式规范下, 不同语义, 不同抽象层面的节点可以共存在同一个形式体系中, 可以用通用的方式进行定位,校验, 转换等. Witrix平台在动态xml方面发展了一系列技术, 为文本处理引入了更多应用相关的规则, 增强了文本描述的抽象能力和表达能力. 
  
   5. 文本作为描述(description)而不是执行指令(execution). C语言的源代码与机器码基本上是一一对应的, 即源代码本身所表达的就是指令的执行过程. 而在Web应用领域, HTML语言仅仅是声明界面上需要展现什么, 但是如何去实现是由通用的解析引擎负责,它并不是我们关注的重点. 描述需要结合诠释(解释)才能够产生实际的运行效果, 才能对现实的物理世界产生影响.这在某种程度上实际上是延迟了执行过程. 一种描述可以对应多种诠释, 例如同样的元数据在前台可以用来生成界面,在后台可以用于生成数据库, 进行数据有效性校验等. 在特定的应用领域中,执行引擎可以是通用的, 可以独立于描述本身不断演化, 因此一种领域特定的描述,它所承载的内容并不是固化的, 而是可以随着执行引擎的升级不断增强的. 例如, 在Witrix平台中, FlowDSL本身所做出的流程描述是稳定的, 但是随着流程引擎不断改进,不断引入新的功能,所有使用DSL的已实现的应用都同步得到升级. http://canonical.javaeye.com/blog/275015
  
   6. Text = Process(Text) 这个不动点在Unix系统中得到了充分的应用: 多个小程序通过管道(Pipe)组合在一起, 可以完成相当复杂的功能. 一个更加丰富的处理模型是 XML = Process(XML). 文本描述很自然的支持多趟处理, 它使得我们可以充分利用全局知识(后续的处理过程可以使用前次处理过程收集的全局信息), 可以同时支持多个抽象层面(例如DSL的不断动态展开). Witrix平台中的编译期运行技术实际上就对应于如下简单规则: 编译期运行产生文本输出, 对输出文本再次进行编译. 通过这一递归模式, 可以简单的实现动态解析与静态描述之间的平衡. 模板(Template)技术是具有关键性作用的文本生成技术. out.write("<div>");out.write(value);out.write("</div>");这种 API拼接方式显然不如<div>${value}</div>这种模板生成方式直观且易于使用. 在Witrix平台的tpl模板语言中, xml的规范性使得在多趟编译过程中我们一直可以维持某种形式约束.
  
   7. 不是所有的情况下都应该使用文本. 普元EOS中所鼓吹的XML总线之类的技术是我所极力反对的. http://canonical.javaeye.com/blog/33794

posted @ 2009-01-04 00:55 canonical 阅读(1815) | 评论 (1)编辑 收藏

    代码生成(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的层面上都可以通过类似继承的操作实现局部调整,这同时也包括在不同的抽象层面上都能对模型进行合法性校验。
   

posted @ 2008-11-23 11:57 canonical 阅读(1662) | 评论 (0)编辑 收藏

   软件系统的构建之所以与建筑工程不同,无法达到建筑工程的精确性和可控性,其中一个很重要的原因在于建筑的产物是一个静态的结构,建筑的过程主要是采用各种预制件填充某个规划好的建筑空间,而软件是一种动态运行的产品,它的各个组成部分之间的关系不是可以静态描述的,而是存在着复杂的交互关系,而且软件在运行的过程中还需要根据需求的变化进行动态的调整,这种动态性使得软件开发中很难抽象出固定的预制件,很难像建筑工程那样实现标准件的组装。现在所谓构件技术的构件插拔图景其实是具有误导性的。

    但是从另外一方面说,软件内在的动态性使得它可以具备更强的适应能力,如果所编制的软件把握住了业务机制的核心内容,则在运行过程中只需要进行少量调整就可以应对大量类似情况。100栋类似的建筑需要花费100倍的建造费用,而100个近似的软件需求的满足可能只需要花费2至3倍的开发费用。现代软件企业在研发过程中都在不断的追求自身产品的平台化,其目的正在于以不断提高的适应性来应对不断变化的客户需求。我们所面对的要求不仅仅是精确把握需求,而是要深刻把握需求背后所对应的业务机制。

posted @ 2008-09-01 23:24 canonical 阅读(342) | 评论 (2)编辑 收藏

  AOP(Apsect Oriented Programming)概念的正式出现也有一些时日了,但是它在程序构造过程中似乎仍未找到合适的切入点,一般系统的设计实现很少将AOP作为必要的技术元素。AOP作为一种普适的技术思想,它所代表的是程序结构空间中的定位和组装技术。http://canonical.javaeye.com/blog/34941 AOP使我们可以通过非侵入性的方式动态修改“任意”已经构建好的程序,而不需要事前有大量的设计准备。原则上说,这种技术思想是可以在任何程序语言基础上进行表达的,并不是只有java, C#这样的面向对象语言才允许AOP操作. Witrix平台中所应用的部分技术与AOP有些类似,只是大量的结构调整表现为xml生成和xml变换,在具体的使用方式上也有一些微妙的差异。http://canonical.javaeye.com/blog/126467

  相对于通用程序语言,xml语言其实是AOP技术的一个更加合适的形式载体。
1. xml格式特殊的规范性确保了在最细的逻辑粒度上,程序结构也是可识别的,可操纵的(在这一点上非常类似于函数式语言)。而所有的命令式语言(imperative language)中,函数内部的结构都是很难采用统一方式进行描述和定位的。
   <ns1:loop>
     
<rpt:Row/>
   
</ns1:loop>

2. xml节点的坐标可以采用xpath或者css选择符等通用方式进行描述,而一般程序结构无法达到xml格式这样的均一性,其中的坐标定位方式要复杂得多。
3. xml节点上可以增加任意属性,不同的属性可以属于不同的命名空间(namespace),这些属性可以辅助AOP的定位机制。而一般程序语言中如果没有Annotation机制, 则定位只能依赖于函数名和类名(函数参数只有类型没有名称),而类名和函数名随时可能因为业务变化而调整(不是专为定位而存在), 由此构建的切点描述符是不稳定的。
 
<ui:PageTable pager="${pager}" cache:timeout="1000" />
4. xml节点的增删改查显然要比字节码生成技术要简单和直观得多。
   

   AOP技术难以找到应用的一个重要原因在于很多人机械式的将它定位为一种横切技术,认为它的价值完全在于某个确定的切面可以插入到多个不同的切点,实现系统的横向分解。而在实际应用中,业务层面上很少具有可抽象的固定的共同性,我们所迫切需要的一般是对已有程序结构进行动态扩展的一种能力。横切是AOP的一种特殊的应用,但不是它的全部。相对于继承(inheritance)等依赖于概念诠释的结构扩展机制,AOP所代表正是对程序结构空间进行任意操纵的一种能力。AOP可以为基础结构增加功能,改变原有功能实现,也可以取消原有功能实现,它不需要把所有的扩展逻辑按照树形结构进行组织,不要求在基础结构中为扩展编写特殊的代码。这种自由的结构扩展能力在Witrix平台中被发展为“实现业务代码与平台基础架构之间的动态融合”。

   在Witrix平台的实际应用中,AOP的切点匹配能力并不是十分重要。一般情况下我们主要通过整体结构规划来确保控制点意义明确且相对集中,因此不需要额外通过切点匹配进行业务功能的再组织,不需要再次从杂乱的程序逻辑中重新发现特殊的控制点。例如在Witrix平台的Jsplet框架中所有后台事件响应都通过objectName和objectEvent参数触发,在触发后台事件响应函数之前都会调用bizflow文件中的beforeAction段。
   在bizflow文件中,aop操作是明确指定到具体函数的,使用模糊匹配在一般情况下只会使问题变得不必要的复杂化。例如扩展actQuery函数
   <action id="aop-Query-default">
    
<source>
       通过自定义标签抽象出多个Action之间的共用代码
      
<app:DoWorkA/>
    
</source>
  
</action>

   
   在Witrix平台中结构组装主要是通过自定义标签库和extends算子来实现,它们都依赖于xml格式的规范性。
1. 通过在custom目录下实现同名的自定义标签,即可覆盖Witrix平台所提供的缺省标签实现,这里所依赖的并不是复杂的匹配过程,而是自然直观的映射过程。http://canonical.javaeye.com/blog/196826
2. 所有的xml配置文件支持extends操作,它允许定制两个具有业务含义的xml节点之间的结构融合规则。例如<biz-flow extends="docflow">。

   实际使用中, AOP技术的一个应用难点在于状态空间的管理问题。一般interceptor中所能访问的变量局限为this指针所携带的成员变量,以及函数调用时传入的调用参数。interceptor很难在状态空间中创建新的变量,也很难读取在其他地方所产生的状态变量。例如对于如下扩展 A(arg1); B(arg2); C(arg3); =〉 Ax(arg1); B(arg2); Cx(arg3); 因为原有的调用序列中没有传递额外的参数,因此A和C的扩展函数之间很难实现共享内部变量x。在TPL模板语言中,tpl本身是无状态的,状态变量通过外部的$thisContext对象统一管理。通过这种行为与状态的分离,结合灵活的变量作用域控制机制,可以以比较简单的方式实现扩展函数之间的信息共享。

posted @ 2008-07-07 00:12 canonical 阅读(1548) | 评论 (0)编辑 收藏

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