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

 元数据(meta)是描述数据的数据。它所描述的有一部分是数据本身的特性,如字段的长度,精度等,另外一部分描述的则是我们使用这些数据的可行方式和目的等。使用meta可以在程序中更加清楚的表达出我们的意图。例如现在需要在界面上显示一个列表,我们的意图未必是要在界面上显示指定的字段A, 字段B,字段C对应的列,而是"显示那些应该显示在列表中的字段"。这一看似同义反复的表述,如果采用元数据表达,则成为 <ui:PageTable fields="${dsMeta.listableFields}" />。通过使用元数据,我们可以做到系统中众多的功能可以共用实现,即通过同一个页面应用不同的meta则得到不同的最终展现,而后台一个通用的DaoWebAction通过使用meta可以完成对所有实体的操作。这也可以看作是一种复杂的策略模式的应用。

posted @ 2007-08-19 13:39 canonical 阅读(1184) | 评论 (2)编辑 收藏

  ORM(Object Relational Mapping)技术为什么是有效的?对这个问题一般的答案是ORM解决了面向对象技术和关系数据库之间的阻抗匹配问题。但是任何一种成功的技术,它的支持理由都不会是单一的。在Witrix平台的实践中,ORM的如下几个特性是关键性的:
  1. 主键和对象之间的一一对应关系。在Web应用中,前台浏览器持有的只能是对象的某种表示, 因此一种locator机制是最基础的要求。
  2. Container结构。Container所拥有的信息包括其所有元素以及元素之间的关系。因为Container具有全局知识,所以它可以解决对象图中的循环依赖问题。正是因为session中一级缓存的存在,才能在实体延迟加载的情况下保证对象引用的唯一性,保证a.b.c.a == a。在程序设计中,所有支持对象图的Container结构都是non-trivial的,都必然是有价值的。例如spring容器在配置管理方面最重要的价值就在于可以管理循环依赖的对象创建过程。而在witrix平台的前台所定义的ControlManager管理器其核心作用就在于协调前台控件之间的消息响应依赖关系。从spring的配置文件可以清楚的看出,bean的声明过程是顺序进行的,但是bean的创建过程是非顺序结构的。在数学上,我们说系统的拓扑(topology)发生了变化。
   A a = new A();  B b = new B();  a.setB(b); b.setA(a);
  3. 对象作为复杂属性的集合。关系数据库中保存的都是原子性数据,每一列都是不可再分解的原始数据类型,数学上称之为标量(scalar). 而ORM引擎返回的直接就是嵌套的复杂数据类型,因此不再需要一个额外的造型过程.虽然总是返回全部数据列不是很优化,但是这种强制性的较粗的处理粒度使得前台程序可以有更多的选择自由.
  4. 对两两关系的内蕴表达及充分利用.关系数据库中所保存的是系统分解后的表示,即关系被分解了而不是被表达了,外键对数据关系只起到一种约束作用,它对于查询语句的构建并没有直接的影响.所有数据之间的关系都必须在查询的时候明确指定出来,即调用者必须拥有数据关系的知识,而不是数据本身拥有这些知识.在如下的sql语句中
   select * from a, b
    where a.fldA = b.fldB
    and a.fldC = 1 and b.fldD = 2
a.fldA = b.fldB 可以称为关联条件,而a.fldC=1可以称作是坐标条件.SQL的复杂性很大程度上来源于我们频繁的需要在各处指定完全一样的关联条件而无法把它们抽象成可复用的组分.在ORM所提供的对象空间中,对象之间的两两关联只要指定一次,就可以在增删改查等各种操作过程中起到作用,特别是在对象查询语句中,可以通过两两关联自动推导出多实体之间的关联关系,虽然推导出的结果未必是最优化的.
  select from a where a.fldC = 1 and a.b.fldD = 2
   在Witrix平台中,我们做了大量的工作以确保对象上的复合属性(例如a.b.fldD)和本征属性(例如a.fldC)在使用上是完全等价的.这些工作的结果并不仅仅是减少了一些应用层的代码量,它使得系统结构发生了深刻的变化.复合属性把单表模型推进到了业务主题模型,使得单一业务对象可以聚集某一范围内的相关结构信息,这才使得witrix的模型分解技术成为可能。因此在我们看来,HQL是hibernate价值的集中体现.
   5. POJO提供了纯粹的first class的持久化结构空间.采用POJO结构可以充分利用现有语言及开发工具中的一系列基础设施,大大降低了持久化结构的构造成本.而透明的get/set操作使得我们可以完全基于相对知识对持久化结构进行变换,在完全不依赖外部环境信息(例如数据库连接和ResultSet界面)的情况下解决系统的主要业务结构问题.这一切成为可能在技术上都源于AOP(Aspect Oriented Programming)所引入的重新诠释的能力,它使得我们可以将对象上的某种相对操作重新诠释为对数据库的相应操作.
   http://canonical.javaeye.com/blog/37064
     6. Entity具有活动能力.Entity并不是完全被动的数据容器,而是可以定义复杂动作的对象.在Witrix平台中,后台程序大致具有如下结构
        Entity  ---- 结构问题
      Handler ---- 业务动作定义
      BizFlow ---- 动力学问题
 Handler类似于J2EE中传统的Service层,只是一般实现的方法要少的多.而BizFlow是某种结合了界面表示的流程引擎.基于实体结构使得系统在细粒度处具有某种活动能力,便于我们构造一些局部结构来解决问题,因而也就缓解了大量操作在Service层的堆积过程,有利于维护系统整体结构的对称性.
    -----------    ==>   -------------|--
    -----------                               |--
    
    通过对于ORM技术的理论分析,Witrix平台采取了一些和一般J2EE架构不同的设计.实际上目前J2EE架构下的常见的DAO模式在使用ORM技术的情况下往往不是合适的选择,因为DAO的一般设计是封装某个实体相关的操作,它直接破坏了ORM的container结构。原先我们只需要EntityManager.get(entityClass, id)这一通用方法既可得到各种数据实体对象,而现在需要对每种实体调用不同的Dao函数,显然这是对系统结构的重大破坏。在Witrix平台的设计中没有独立的DAO层,它通过通用的EntityDao统一完成所有对象的存取过程,而不是每个XXXManager继承公共的Dao类。即整个系统架构中尽量维护数据存取过程的统一性而不是实现它的分散化。
  在Witrix平台的Workflow引擎,Wiki引擎等模块的设计中,IWorkflowStore和IWikiStore等类的设计类似于DAO模式,是对存取方式的一种封装。在比较复杂的模块中,对于存取逻辑做出一定的封装是需要的。但是注意到Store类的设计和实体框架的设计相比,其结构可分解性要相差很多,它基本上只提供对外的服务接口。如果我们能够对于文件系统等存储设施作出充分多的工作,我们一样可以对于非关系数据库的某些存取形式完成Container结构,只是这个工作量过大,而我们一般并不需要对非通用的存取结构掌握如此充分的知识。
     实体结构隐含的扩展了系统的同时性视图,a.b.c.a == a 所隐含表达的事实是a,b,c是同时存在的.http://canonical.javaeye.com/blog/33797 在某些时候,例如当我们需要将系统结构顺序化,序列化的时候,这种同时性会成为一种障碍.因此Witrix平台中数据同步所使用的ETL(Extract Transform Load)引擎是基于表结构(分解后信息)的,而不是基于实体结构(关联信息)的.实际上,关系模型在某种意义上是系统分解后的必然结果,因此随着我们对系统的理解的粒度要求越来越精细,很可能最终需要我们明确意识到关系对象本身的存在性,最终实体模型会非常近似于关系模型.只不过在实体模型级列中我们选择的余地更大,关系模型可以看作是它的某种极限.理想的情况是在不同的时刻我们可以使用不同的关系抽象,只是受限于目前的实现技术,在系统构建时刻基础的关系结构往往就被固化下来.

posted @ 2007-08-13 00:25 canonical 阅读(1018) | 评论 (2)编辑 收藏

   JSF(Java Server Faces)技术从发布时间上看已经是一种比较古旧的技术了,但是目前仍未能成为主流的开发实践。从我知道这种技术开始, 我对它的判断就与我最早对于EJB的判断一样, 它们都在某种程度上捕获了真正的需求,但是因为它们自身诡异的技术路线.我很怀疑是否这些标准制定者故布疑阵, 便如Microsoft的OLE技术一样, 故意抛出一个错误的方向, 将大批组件开发商带入死局.
   JSF技术是一种双重的存在:它首先是一种标准,然后也提供了一种缺省的实现。但是从这两方面,我都无法看到JSF的未来。
   从设计上说,强类型的视图模型对象层与Witrix的架构设计原则严重冲突。Witrix的基本架构是浏览器和后台服务器通过具有显明语义的url实现两分,这也是所谓REST风格的一种内在要求。隐蔽了链接的技术破坏了基本的web超链模型. 为了获得那么一点点结构控制能力, 做出这样的抽象是不合适的.JSF的配置模型继承了structs的传统,仍然是那样的冗长繁杂。我们是否真的需要这些配置文件,还是希望像ROR那样在代码中直接完成一切?
   不能在标准的浏览器中预览. 可以说创造了一个JSF IDE的市场, 但是这无疑是一个无聊的市场. 现在有一些备选的方案, 如Facelets, 使得jsf可以采用属性语法, 但是只要想想仅仅为了这么一点小小的修正所需要付出的开发量就足以让人崩溃。
   JSF提供了组件级别的事件响应机制,因此似乎是AJAX应用的理想场所.但从Witrix平台的开发实践来看,JSF对于AJAX的使用是受限制的,有着很大局限性的.组件事件响应并不一定要采取JSF那种体系结构.
   从实现角度上说,基于jsp tag可以说是JSF的致命弱点之一. jsp tag从设计之始就一直是未经过实践考量,其设计无法支撑复杂的控件架构. 特别是早期JSF与标准的JSP tag不能互通实际上是明显的设计缺陷, 而且性能问题是内置在该设计中的. 现在虽经多个版本的不断补救, 但是为了兼容性, JSP Tag负担过重, 它始终是基于文本处理模型,实际上不可能有什么本质性的进步. JSP tag模型过分孱弱必然造成JSF设计中大量处理过程堆叠到界面对象层,更加剧了JSF的模型复杂度和性能瓶颈。 实际上根据Witrix平台中tpl模板技术的设计经验,大量界面构建过程是可以在模板层以直观的方式完成的,而不需要借助视图模型对象。
   所有问题的一个集中体现就是增加一个新的JSF组件绝对不是一件平凡的事情.如果有一天这个问题可以得到解决,那时的JSF从思想和实现上都必然和现在的JSF有着本质性的区别.

posted @ 2007-07-29 23:43 canonical 阅读(1297) | 评论 (7)编辑 收藏

  REST(Representational State Transfer) 是Roy Thoms Fielding在其博士论文中所提出的一种架构风格(Architectual Style)。http://roy.gbiv.com/pubs/dissertation/top.htm  http://www.redsaga.com/opendoc/REST_cn.pdf 可以说它也体现了整个互联网架构的基本设计原则。随着AJAX等技术的发展,互联网的资源语义逐渐得以恢复,最近对于REST的讨论也热烈起来。http://www.ibm.com/developerworks/cn/web/wa-ajaxarch/
  在Fielding的论文中对REST有如下陈述:
The name “Representational State Transfer” is intended to evoke an image of how a well-designed
Web application behaves: a network of web pages (a virtual state-machine), where the
user progresses through the application by selecting links (state transitions), resulting in
the next page (representing the next state of the application) being transferred to the user
and rendered for their use.
  点击超链接之后,服务器端返回资源的某种表示(Resource Representation), 客户端的状态(Representational State)随之发生迁移(transition),在服务器端返回的表示中存在着到其他状态的迁移链接,使得我们始终自动具有进一步迁移的能力(这和ajax中所谓返回纯粹数据其实是不同的). 这种图景其实也正是Tim Berners-Lee最早所设想的web的图景。因为REST是对互联网架构设计的一种抽象,因此所谓的Restful其实就是尽量符合现有Web标准。从概念上说,REST所关注的主要还是分布式资源信息交换,而不是对复杂的业务逻辑进行抽象。遵循REST将会使整个互联网受益,确保路由器,缓存,代理,浏览器,服务器等各个组件可以协同工作,它的意义并不是针对单个应用程序的。不过与witrix体系架构相对比,我们还是可以发现REST架构风格对于一般web程序的参考价值。
   1. 通过唯一的,确定的url来标定可访问的资源。url定义了后台资源的访问界面,明确区分了浏览器和服务器两个分离的状态空间。这种两分的架构约束是witrix平台最重要的设计原则。在witrix平台中,Jsplet, BizFlow, PageFlow,AuthMap等技术完全体现在url的精化设计上,系统中所涉及到的所有关键概念都对应于url中的明确组分。而JSF等技术通过对象封装模糊了资源的访问界面,迫使我们只能通过一个错综复杂的对象包络来理解系统的交互过程。
   2. REST强调整个交互图景中所有需要的相关信息都是in band的,而且语义上不同的部分都对应于特定的语法上不同的部分。例如为了保证url的不透明性和稳定性,一些信息通过http协议的header来传递。这里强调的语义自明性实际上是脱离具体知识体系的形式上的可区分性。在现有的服务器端程序中, 因为直接接触到的是已经解析好的parameter集合, 因此Witrix平台中关注的是在参数集合中识别出特定的形式结构, 这通过EngineURL对象实现.
   3. HTTP中明确区分了GET/POST/PUT/DELETE四种标准操作, 并对它们的语义进行了精确的限定. 而所谓的RPC(Remote Procedure Call)与此是不同的. RPC上承载的方法数可以是无限多的,但是这种无限多的操作反而从某种层面上说是简单的,是对称的,没有可区分性。而在REST中,在确定资源这一限制下,我们可以区分出GET/POST/DELETE/PUT这四种有限但是不同的语义。 是否这四种语义构成完备的操作空间?从某种意义上说, 这四种操作覆盖了资源的整个生命周期, 它自然满足了某种完备性. 但是抽象意义上的完备性并不意味着它在物理层面上也是完备的. 我们都知道二维空间是完备的,但是二维描述并不是三维空间的合适表达. 在Witrix体系下,WebAction所提供的完备的操作集合包括Query, ViewDetail, Update, Add和BizAction.
BizAction是个特定的扩展,所有不便于直接归类到CRUD操作的操作都可以归类到这一Action的名义下. Witrix架构中把CRUD看作是一个更大的Action空间的一个子空间, 对此子空间的结构进行了细致的划分, 所关注的重点是对部分信息的更明确的表达, 而不是对程序整体的建模.
   区分GET/POST/PUT/DELETE四种操作, 最重要的意义在于定义了GET和其他操作的区别. http://www.w3.org/DesignIssues/Axioms.html
 a. in HTTP, GET must not have side effects
 b. in HTTP, anything which does not have side-effects should use GET
   在GET这种语义下, 才可能建立独立的cache组件, 而正是因为cache的存在, 才使得web成为infomation space而不是computing program. 而在Witrix平台中, 很多通用机制的建立(例如精确到数据行的访问权限)都需要对于CRUD做出精细的区分, 而不仅仅是识别出GET操作.
  4. REST中虽然定义了具有概念稳定性的url, 但是因为操作集合有限,而且强调服务器端的无状态性, 因此一般认为这种结构并不是面向对象的. 不过,在我看来,面向对象首先意味着一组相关性的聚集, 实际上从语义层面上看, REST与witrix平台的jsplet框架非常接近, 最大的差别在于服务器端明确定义的thisObj---this指针. 服务器端的无状态性对于网站应用而言是必要的, 但是对于复杂的企业应用而言并不是合适的约束.
  5. 对整个互联网应用而言,URI应该是不透明的,它的结构对互联网中间应用而言应该是不暴露的. 资源的结构是与整个互联网架构无关的. 最近业内鼓吹REST的时候往往也推崇所谓beautify的url, 例如 http://www.my.com/blog/3/comment/1. 这种结构不过是维护url稳定性的一种副产品, 在我看来, 它对于REST而言并不是必需的. 不过根据这些年关系数据库应用积累的经验,识别集合和集合中的个体是一种非常通用的场景,因此我们可能规范化这种结构,甚至搜索引擎等外部组件也可能理解这种语义,从而实现更加复杂的语义网络。在现有的所谓支持REST的web框架中, 往往支持这种规范化的url直接映射到后台的响应函数上. https://cetia4.dev.java.net/files/documents/5545/38989/cetia4_tutorial.pdf. 例如在cetia4框架中, GET /blog/3将会被映射到后台函数 String render(RenderContext context, int id)函数上. 在witrix平台中, 缺省并不采用beautify的url, 但是因为对于语法成分具有明确的定义, objectEvent=ViewDetail&id=3这样的url将映射到后台biz-flow中的action段.
 <action id="ViewDetail-default">
  <source>
    在这里直接拿到entity对象,而不是id值
  </source>
 </action>
在action中我们直接接触到的不是id值,而是id值相对应的实体对象本身. 对于objectEvent=Remove, 我们可能一次删除多条记录, 则在后台bizflow中action=Remove-default段将会被调用多次,每次处理一个实体. 这些自动处理机制都离不开对于标准化url组分的明确理解.
   在网站应用中, 我们一般也通过url rewrite机制来支持简化的层级url. 但是这种根据位置来确定语义成分的url格式其实也存在着很大的局限性, 在cetia4的映射中很多时候都会出现含混的情况. 而且因为资源是抽象的, 页面中的相对路径计算会出现一定的困难. 在witrix平台中, 通过tpl模板语言的标签增强机制, 我们重新定义了页面中的相对路径计算规则, 从而大大简化了资源管理问题. 在tpl中相对路径计算永远是基于当前模板文件的, 例如对于通过<c:include src="subdir/included.tpl" />引入的子页面included.tpl, 在其中的<script src="included.js" />等使用相对路径的语句会在编译期被转换为使用绝对路径, 生成<script src="/contextPath/root/subdir/included.js" >等语句.
  6. 一旦url格式被规范化, 我们就可以基于它在应用程序中发展很多全局结构. 例如在cetia4中, 可以建立全局的navigation模型, 并提供了一个breadcrumb 导航栏. 这是一种全局的链接管理机制,在一定程度上实现了导航信息压缩.  但是因为其没有对象结构支撑, 与Witrix平台的PageFlow技术相比, cetia4的方式不过是非常原始的一级策略, 它对于对象生命周期的管理也是过于简陋的.http://canonical.javaeye.com/blog/32552
  7. REST中强调generic interface 而不是强调custom interface. 实际上目前业内的对象化方案很多时候都沉迷于提供特定结构的构造方法, 很多面向对象框架本身就在构造各式各样的特定结构。一种通用的结构只存在于概念中,是复杂结构背后的事情。但是在很多情况下, generic interface无论在实现成本还是概念成本上都是更加低廉的. 在witrix平台中, jsplet框架所实现的对象化就是一种generic方式的,弱类型化的, 而不是强类型的对象结构的。
  8. 关于REST的另一个有趣的问题是如果大量使用HTTP内置特性,是否我们的应用将严格绑定到http协议上,不再是所谓的protocol independence。不过我们真的需要同一语义模型的不同实现吗.

posted @ 2007-07-08 22:06 canonical 阅读(1831) | 评论 (3)编辑 收藏

    今天adun给我讲了一个他所谓可退化的设计,在我看来问题还是多多。从直观的角度上说,在java中声明一个具有多个参数的函数,调用的时候对于不需要用到的参数都传入null, 这不是理想的可退化场景。所谓的退化不仅仅是概念层面的,不仅仅是关于语义的,很大程度上它也是形式上的,是关于语法结构的。
    理想的退化场景是尽量维持形式/结构稳定性的情况下实现诠释范围的缩减,在任何层面上都不需要知道超出当前需要的信息。而如果我们被要求必须传入自己实际上不需要使用的参数,则必然存在着一定程度上的信息泄漏。一个朴素的看法应该是,当我们需要它是一个参数的时候它就是一个参数,当我们需要它是三个参数的时候它就是三个参数。对于系统形式结构的有效规划是实现可退化性的前提条件。


posted @ 2007-06-27 22:54 canonical 阅读(893) | 评论 (3)编辑 收藏

    描述所关注的是“what”,而运行所关注的是“how”。在现代软件开发中,描述信息作占的比重日益加大。甚至一种极端的倾向是把所有业务逻辑都写在各种格式的配置文件中. 配置文件目前多采用xml格式,它的优点是自说明的:属性名直接标示了其基本含义,但是这也在一定程度上加重了命名的负担, 造成了配置文件的臃肿。因为在普通的程序语言中,可以用来传递信息的结构更加丰富,例如参数的相对位置,参数类型, 匿名函数, 指针引用等。而一般配置文件中没有定义合适的继承,封装等抽象机制,很难如同普通程序语言那样进行有效的结构压缩。
    在很多灵活的弱类型语言中,借助各式语法糖(syntax sugar)可以实现描述性的运行结构, 或者可以看作是构造性的描述, 它在部分程度上消解了描述的诠释问题, 不需要额外的解释器即可实现描述结构的解析. 这有些类似于编译理论中的语法制导翻译, 在动态结构组装方面具有明显的优势. http://www.blogjava.net/canonical/articles/19697.html. 但是独立的描述信息仍然是有着重要作用的, 关键是作为元数据存在的描述信息可以以多种方式被使用, 并可以被部分使用. 此外一些特殊设计的描述文件可以很自然的汇集系统各个方面的信息到同一层面加以展示,而一个通用语言无论语法如何灵活, 抽象能力如何强大, 毕竟受限于先天的结构, 要做到这一点还是不现实的.
    在witrix平台中配置文件的设计一般是综合考虑静态描述和动态调整的需要, 在设计上分成静态描述段和动态运行的init段, 系统将确保init段中的tpl代码会在适当的时候被调用.


posted @ 2007-05-27 18:48 canonical 阅读(1229) | 评论 (1)编辑 收藏

   在商业产品开发中,如何有效的控制同一产品的多个衍生版本是一个非常重要的问题。客户的需求是多样化,差异化的。这些差异有些很小,可以通过参数配置,资源装载,skin切换等方式加以吸收,而有些则要求对界面布局和程序逻辑等作出较大调整。Witrix开发平台在系统基础架构方面为程序的客户化提供了有力的支持。
   1. 多版本控制的关键首先在于系统良好的模块划分。因此Witrix平台的beans,auth-map(权限归约规则)等配置文件格式都支持import/include等基础的分解策略,字符串资源和错误码映射等支持多重定义文件,而对于sql.xml(外部sql语句定义), meta.xml, biz.xml, hbm.xml等配置文件采用分模块动态装载机制。
   2. 在Witrix系统中定义了一个特殊的custom目录,规定了一般性的覆盖规则:custom目录作为系统根目录的影子目录,如果custom目录下存在同名文件,则优先装载custom目录下的文件。例如,如果custom目录下存在/_config/my/my.biz.xml文件,同时在根目录下也存在/_config/my/my.biz.xml文件, 则实际装载的是custom目录下的实现。这里的一个关键在于只有meta.xml(元数据),biz.xml(BizFlow描述文件),.lib.xml(tpl模板库)等具有一定完整性的文件才支持custom机制,而并不是所有资源都采用custom机制。如果每一个tpl文件,css文件,js文件等都优先从custom目录下装载,则很快就会出现循环引用,相对路径计算将会变得非常混乱,更重要的是我们将无法定义资源删除语义。
   3. 元数据文件,BizFlow描述文件,PageFlow描述文件等都支持复杂的extends机制,使得我们在扩展时只需要对于系统差异部分进行描述,而不是大段拷贝代码。
   4. tpl模板库和sql-map机制等采用的是追加覆盖策略。例如custom目录下的ui.xml标签库文件并不是直接覆盖系统根目录下的ui.xml文件,而是按照标签名进行细粒度的覆盖。系统编译时会自动检查覆盖标签的所有参数要求和原标签相兼容(例如允许增加参数而不允许减少参数),确保所有引用到原标签的tpl代码仍然有效。实际上整个witrix平台多版本扩展机制的一个设计目标就是确保平台主系统向各个分支产品的单向信息流动。在具体的表现上就是我们随时可以拷贝平台主系统覆盖到分支产品的相应目录,所有扩展实现与主系统实现保持分离状态。当然为了保持设计的弹性,系统中也定义了开关参数用来有选择的跳过一致性检查。
 

posted @ 2007-04-22 23:15 canonical 阅读(1494) | 评论 (4)编辑 收藏

    命名(Naming)无疑是人们认识世界最基本的手段之一。从软件技术的发展中我们也可以观察到命名技术的不断深化。
    1. 助记的名:汇编之中我们有了move/push/pop这样的指令,所面对的不再是010101这样的同质的数字世界。变量也逐渐可以拥有自己的名字,甚至多个名字。在C语言中指针的概念被进一步抽象化,使得我们可以为任意内存地址起一个可读的名字。我们甚至渐渐忘怀了pStruct是指针的名字,而直接把它等同于指针所指的内容本身。
    2. 局部的名:函数(例程)概念的出现把局部名称引入系统,从此精细结构的发展才成为可能。
    3. 多义的名:面向对象的出现可以看作是命名技术的一种重大进展,它可以把一组相关的数据和函数放在一起起个名字。继承概念为名引入了多义性。通过虚拟函数表所实现的lazy-binding部分松动了对象的名和实之间的指称关系。现在一些所谓dynamic dispatch技术,依然是顽强的希望在同一名下,纳入更多实的变化。
    4. 特指的名:面向对象技术创造一个特殊的名---this指针。它是一种约定了的固化了的局部名称。使用this指针使得我们区分了领域(domain)的内外。在domain外对象可以有各种称谓,而domain内我们直接通过this直接触及到对象的实体。在javascript这样的动态语言中,函数和this指针是动态绑定的。在某种意义上,这意味着一个操作所依赖的domain知识可以是动态变化的。
    5. 相对的名:面向对象技术所创造的知识相对化概念的一个直接体现是命名的相对化。一个函数它的具体含义不再是绝对的,而是相对于this指针的。因此我们不再采用user_load, book_load这样的全称的函数名, 而只定义load这样的具有依赖性的函数。在面向对象的理想操作图景下,首先应该是通过一个整体的参数直接区分出多个大的情景,然后在每个特定的情景下分别调用相对函数进行操作。在模板(template)技术或者动态语言中,这种相对性可以得到更加充分的发挥。因为在泛型或者弱类型语言中,我们需要的只是对象提供特定名称的函数或属性而已。
    6. 持久的名:在早期的语言中,名只在编译时刻存在。在编译出的二进制代码中,名所提供的丰富的描述空间不复存在,我们所有的只是同质性的二机制地址而已。而在现代语言中,反射已经成为了不可或缺的技术,它使得我们在运行时刻仍然可以拥有复杂的描述结构。
    7. 分离的名:在一般的程序中,我们早已习惯了变量名直接指代实际可操作的对象本身,名的问题显得太平庸以至于大家似乎忽略了它的存在。但是在web体系架构下, 因为存在着浏览器和服务器这样两分的状态空间, 名成为两个系统交互的直接手段,名的重要性也重新凸显出来。只有在一个封闭的container中,才能通过名字解耦. 因此web架构设计的一个核心问题是构建出各种各样的全局的container. 浏览器前端技术的一个本质性困难即在于多个浏览器窗口之间没有共享的全局对象空间,因而很难在前台独立建立container结构。
   在witrix平台的jsplet框架中,在前台我们通过如下url来访问后台
  /view.jsp?objectName=MyObj&$bizId=test&objectEvent=ViewDetail&id=1
MyObj这一参数标定了BeanContainer中的一个Java对象, $bizId参数指定应用某个Aspect到此对象上,objectEvent参数映射到WebAction上的一个java方法,而EntityManager最后负责把id映射到具体的实体对象。当我们在后台需要编制代码的时候,entity对象已在手边。
    8. 名的结构:当名越来越多的时候,我们需要对它们进行有序的组织。名字空间(namespace)所采用的树形结构可以说是最直接的一种组织方式。这一结构不仅仅可以用于描述,同时可以用于控制。

posted @ 2007-04-01 21:30 canonical 阅读(1392) | 评论 (3)编辑 收藏

    软件这个领域中传统上占优势的是自vonNeumann以降的数学视角,计算问题是其思想内核,而函数式语言无疑是其比较贴切的表现。但是仅有数学,我们对于世界的认识是不充分的。有这样一个笑话。烧一壶水的完整步骤如下:1.向空壶中注满水 2.放到火炉上 3.烧到冒泡。现在有半壶水,求解烧水的步骤。数学家的回答是直接把半壶水倒掉,然后宣称问题已经解决,因为它已经被归结为第一个问题。实际上数学的视角直接限制了某些命题进入研究者的领域。现在业界占主流的面向对象技术,并不像是理论界的自然创造,它的思想来源更像是来自于开发窗口系统的工程实践。http://gagne.homedns.org/~tgagne/contrib/EarlyHistoryST.html.
    面向对象技术的核心是描述问题,它试图实现业务概念和业务关系在程序中的直接表达,所谓更贴近人的思维方式。但是从面向对象的实际操作过程我们即可得知,这里所谓的贴近只是贴近人们的常识而已。常识是有意义的,但也是浅薄的。当程序功能变得愈加复杂,程序的结构不再那么直观的时候,我们从面向对象理论得到的支持并不如最初它宣称的那样的巨大。早期面向对象技术所提出的枚举系统中所有名词和动词的设计方法现在看起来无疑是非常的幼稚。如何确定一个系统中到底需要定义多少个对象,以及如何确定它们之间错综复杂的交互关系,这些并不是通过我们的业务常识能够确定的。
    在具体的,特定的常识与抽象的数学之间,存在着所谓的物理学。常识总是和一定的“有意义”的场景绑定着,可以直观的理解。而数学则蒸馏出一些纯粹的符号,试图与所有预置的意义划清界线。物理学是折衷主义的,或者说是非常狡诈的。它选择了只在需要的时候诠释。在物理学的推导中,大量的中间过程都是数学性的,难以寻找到明确的物理含义的,但是我们无视它,只对某个最终结论加以解释。事实就是公式千千万,但是我们却只对其中的某些说:嗯,这里面有物理。与此对应,在软件开发中,将直观的业务需求映射到通用的程序语言实现时并不是那么直接的,在这之间可以存在着超过一般人想象的与特定业务无关的厚重的技术层。在这个层面中可以定义出非常复杂的交互模式,并在不同的特定场景下将其诠释为不同的业务应用。这也正是平台技术赖以生存的基础。
    丰富的物理学的根基在于丰富的物质结构,它的核心是动力学。实际上单量子体系并不如想象中那样难以研究,这个领域充斥着粗鲁的线性近似,而它们的预测精度却都难以想象的高。真正的复杂性来自于简单元素所构成的复合结构,以及这些结构之间的相互作用。随着ROR这样的动态语言框架的流行,很多人把它们的成功归结为语言特性的增强,这在我看来并不是富有成果的方向。诚然,更多的语法糖(syntax sugar),更多的动态性可以降低我们构建某些复杂结构的代价,但是这种降低最多是减少一两倍代码录入量,而绝不可能是数量级上的影响。对于程序开发可以起到决定性作用的是那些复杂的大范围结构,而不是通用语言本身所提供的那些抽象的简单的局部结构本身。当然,一般人大概很少有能力做出超过一定难度的创造,一般都只是依赖现有的语言,现有的框架以直白的方式实现功能,因此很难想象可以在语法特征更少的java中轻易实现超过ROR的开发便捷性。
    通用语言的发展必然是有极限的, 也必然是贫瘠的, 因为它无法利用有限场景下的信息. 而DSL(Domain Specific Language)将注意力集中在某个特定的领域(domain)中,便可以名正言顺的引入非常复杂的语法结构. 这些结构旨在本领域中具有意义,而不用担心超出应用范围后遭到"冗余设计"的诟病. 我无法想像在一种通用程序语言中会规定Witrix平台中BizFlow这样的结构设计,但是作为一种domain相关的语言结构,它的表述却无疑是非常高效的。我相信,随着我们对于程序结构的认识的不断深化, 在DSL所构建的复杂结构空间中可以发展出一些真正有趣的技术.
    对于DSL存在着两个常见的误解.一是DSL是存在于特定领域的,因此它是一种受限制的语言,应该在计算能力上弱于图灵机.但其实DSL的核心在于高效的表达,在于直接存在的高阶结构,与它的形式计算能力并无关系.正如物理中Lagrange表述和Hamilton表述在数学上是等价的一样,我们的选择只在于某种情况下某种描述方式会更加方便。这里所玩的游戏更像是概念空间中的拓扑变换。
    关于DSL的另一个误解是DSL的表述形式应该接近自然语言.但事实上数学符号和化学公式都是高效的DSL,它们的表达形式甚至内在逻辑都和我们的自然语言相去甚远.我们是否已经完全解决了程序问题,而只是要把这种能力向无知的客户转授?目前在程序编码的过程中我们仍然面临着大量未决的问题,程序员应该是DSL的直接受益者.此外,西人对于语言的认知是偏狭的,因为他们眼中的language只有拉丁语系和日耳曼语系,而不知道这个世界上还存在着不符合西文语法的汉语。按照朱光潜的诗论,西人长时间认为诗是不宜于写景状物的,因为语言是串行的,因而只适于按照步骤叙事,却不了解汉语的自由组合形式和丰富的单字表现力可以轻易捕获微妙的瞬间.

posted @ 2007-03-18 23:05 canonical 阅读(1637) | 评论 (0)编辑 收藏

   最近D语言发布了1.0版,这是一个由编译器开发者所设计的编译语言,语法类似C++, 但是针对C++的弊病作了大量修正,并增加了很多现代特征,其中还是有一些新意在其中的。http://www.digitalmars.com/d/overview.html 我对其比较感兴趣的部分是D语言明确提出的编译期运行的概念。虽然C++让大众了解了meta programming技术,很多人因此认为meta programming的威力在于类型演算,但是在我看来meta programming的真正作用在于在编译期可以“动态”产生或调整代码结构。它依赖于类型是因为我们只能利用类型来承载一些额外信息, 这无疑也是对于类型的一种滥用。在D语言中template所接受的不仅仅是类型, 或者类型的类型,它可以是任何符号.
  template MixInAttr(T, char[] name){ 
     mixin(" T _" ~ name ~";");
     mixin(" T "~name~"(){ return _"~name~"; }");
     mixin(" void "~name~"(T v){ _"~name~" = v;}");
  } 
 
  template MixInOtherAttr(T, T v){
    T _otherAttr = v;
   
    int otherAttr(){ return _otherAttr; }
  }
 
  const int myValue = 1;
 
  int addFunc(int x){
    return x + 1;
  }
  
  class MyTest{ 
     mixin MixInAttr!(int, "myAttr"); 
     mixin MixInOtherAttr!(int,4);
    
     static if(addFunc(myValue) 〉 0){
        int testFunc(){ return 5;}
     }
  }
 
  void main(){ 
     auto t = new MyTest;
     t.myAttr = 3;
     int v = t.myAttr;   
     v = t.otherAttr;
     t.testFunc();
  }  
  不过现在编译期运行无疑是一个正在探索的方向, 在D语言中的使用方式并不是非常理想的, 在形式和功能实现上都存在着重大改进的可能. 
 
  在witrix平台的tpl模板语言中,我们也引入了编译期运行的概念,只是tpl的语言特征非常简单再加上xml格式特殊的规范性,编译期运行在tpl中的实现是非常直接和明确的.在tpl中定义了<cp:run〉标签,它的内容在编译期运行, 输出结果(xml字符串)将被继续编译. 在<cp:run〉中可以执行任何有效的tpl代码, 它们在编译期状态空间中运行. 例如
 〈cp:run〉
   〈c:forEach var="_x" items="${tagBody.children()}"〉
       bla bla bla...
   〈/c:forEach〉
 〈/cp:run〉
在EL表达式中我们通过cp前缀实现普通调用和编译期调用的混杂.
  〈cp:const class="mypkg.MyConstantClass"/〉
  〈c:eval expr="${myFunc(cp:const.MY_CONST, commonVar, cp:funcInCompileTime())}" /〉
  〈cp:compile test="${exprInCompileTime}"〉
     any tpl ...
  〈/cp:compile〉

 其实当我们把改进的目光开始放到编译期的结构方面的时候, 可以做的工作是非常多的. 例如从结构上看, 继承策略相当是类结构的一种一阶替换策略.
  类B(methodB) extends 类A(methodA, methodB)  ==〉 类B(methodA[inA], methodB[inB])
如果我们放弃概念层面上的纠缠,而如同template那样只关注语法结构, 则可以发展出更加复杂的替换操作.
  NODE_B(                           NODE_A(                                  NODE_B(
    SUB_NODE_A1       〉〉    SUB_NODE_A1             ==〉    SUB_NODE_A1
      SUB_NODE_B11                  SUB_NODE_A11                           SUB_NODE_B11
                                                     SUB_NODE_A12                          SUB_NODE_A12
  )                                          )                                                       )
C++中及D语言中的template技术在某种意义上相当于是在代码结构中预留了空洞, 可以实现跨越类结构的替换填充. 只是D语言挖洞的能力无疑比C++强大的多. 
  如果我们考察的再细致一些, 就会发现两个语法节点的融合也是存在多种策略的.
  B 〉〉 A ==〉 (remove_A, replace_A, insert_B_before_A, insert_B_after_A, insert_B_into_A, insert_A_into_B)
而通过insert_A_into_B/insert_B_into_A策略既可实现所谓的AOP intercept操作. http://canonical.javaeye.com/blog/34941 在witrix平台的BizFlow技术中, 我们通过高级的结构融合策略实现为任意实体引入流程支持. 最简单的情况下我们所需要做的工作只是增加一个语法元素
 〈bizflow extends="myflow"〉
引入流程意味着对界面的一系列修正, 例如增加,删除某些菜单, 同时要增加很多流程相关函数, 对某些函数实施AOP操作, 在各处引入权限控制等等, 这些都可以通过extends操作引入.

  在传统上, 编译器结构是固化的, 它所编译的代码结构是固化的, 而它所编译出的代码也是固化的, 但是现代语言的各种发展正致力于在各个层面引入动态性. 编译期运行抑或是编译期结构操纵技术的引入是在一个层面上打开了潘多拉魔盒. 它让我们看到了前所未有的技术可能性, 但它无疑也是危险的,难以控制的. 我们目前的做法是尽量克制,只在局部使用, 但我相信它在很多语言中都是可以在理论上严格定义,并精确实现的, 我们最终也能够找到合适的形式来约束它的破坏性. 在这个过程中我们需要引入更多的物理化的视角。

posted @ 2007-03-04 18:14 canonical 阅读(1626) | 评论 (0)编辑 收藏

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