posts - 157, comments - 216, trackbacks - 0, articles - 6

2005年12月2日

    在witrix平台中,异常处理没有采用java语法支持的checked exception, 也不提倡使用自定义的异常类, 而是定义了少数几个RuntimeException基类,一般是CommonException(RuntimeException的派生类)。
    在我自己的经验中,checked exception从未发挥过实质性的作用。checked exception在某种程度上破坏了封装性原则。我们一般不会在最细的粒度上处理异常,而是在某个统一的模块节点处进行。如果使用checked exception, 则从最底层的调用到具体异常处理层的整个调用堆栈上的函数都必须明确标记自己不处理该异常,这是完全不必要的负担。这种细粒度上的负担往往将程序员引导到 错误的方向上去,例如编写catch块直接捕获异常
  try{
     ...
  }catch(MyException e){
     e.printStackTrace();
  }
在witrix平台中通过包装类来将checked exception包装为RuntimeException, 而且除了在最终代码处理模块决不屏蔽异常。
 try{
    ...
 }catch(IOException e){
 throw Exceptions.source(e); // 此时会自动trace异常堆栈及异常消息
 }

(后来看到Bruce Eckel的文章Does Java need Checked Exception,发现大家在对待checked exception的态度上倒是心有戚戚焉。)

     一般使用自定义的异常类似乎是要将类名作为错误返回码使用,利用java编译器可以做所谓的强类型检查,这实在是一种概念上的浪费。毕竟创建并维护一个 java类还是有一定的代价的,特别是错误码经常变动而且数量不菲。实际上,java类库的设计中也是尽量重用已有的异常类,例如整个jdbc包只抛出 SQLException异常,xml包只抛出SAXException异常。

     使用异常,常见的方法是抛出一个字符串消息,例如 throw new MyException("the object manager does not contains the object :" + objectName);
这种做法的主要问题是,字符串异常消息无法进行进一步的处理,因而只能直接显示给最终用户,这一方面限制了错误显示的格式和方式,另一方面也不利于程序的多语言支持。
     witrix平台中抛出异常的标准方法为
 throw Exceptions.code(errorCode).param(paramValue).param(paramName,paramValue);
例如
    throw Exceptions.code("web.CAN_err_missing_object_in_manager").param(objectName).param(objectManager);

class Exceptions{
    public static CommonException code(String errorCode){
  return new CommonException(code);
 }
}

class CommonException extends RuntimeException{
 public CommonException param(Object paramValue){
  ...
  return this;
 }
}
      Exceptions规定只使用规范格式的错误码而不是任意格式的异常消息。这样在捕获异常之后,就可以根据错误码和当时的语言Locale设置来决定最终显示的消息格式。
      同时CommonException采用流式设计来支持任意数量的自定义参数。这一方面减少了自定义异常类的需求,另一方面也避免了将参数与错误码混合的倾向,即我们就不会倾向于
使用 throw Exceptions.code("the object manager does not contains the object :" + objectName);

posted @ 2005-12-02 23:00 canonical 阅读(722) | 评论 (0)编辑 收藏

    tag在国内java社区并不算流行,这在很大程度上是因为jsp tag的设计失误造成的。但在整个开发业界内,tag已经成为一种广泛应用的技术。微软的dotNet服务器端极端依赖tag技术,而在浏览器端IE的 behaviour, htc也独立的发展起来。Longhorn的XAML, Firefox的XUL无一例外的依赖于可自定义的tag。java社区的JSF, SiteMesh, Tiles 等等,不可尽数。有些人在前台通过给html原有元素增加自定义属性,然后通过javascript去解释的做法,也是一种element enhance概念的变种。至于FreeMarker这种模板语言,明明类似于tag技术,偏偏不采用xml语法,简直是自找麻烦。
    这里最关键的地方就是自定义tag可以实现抽象层次的提升,是一种类似于函数封装的机制,从而实现概念的分离和明确化。基于tag可以实现页面元素的组件 化,加上xml语法的可理解性,表达能力以及无与伦比的集成能力,使得tag技术可以超越VB等组件开发环境(想想集成别人的组件代码难还是集成别人的 xml描述文件难)。自定义tag提供的抽象能力不仅仅是面向对象的,而且是类似AOP的,这些都极大的辅助了我们的思考和设计。

    cocoon使用管道技术也构造了某种tag机制,但是它的效率很成问题。从数学上说多个处理函数 g, h, k可以通过函数组合(composition)构成新的函数f

    f(data) = g * h * k(data) 

这是所谓函数式语言强大能力的源泉。cocoon处理的时候从k(data)开始,处理完毕之后调用h, 即函数是从右向左结合的。如果我们保证处理函数满足左结合律,则g*h*k就可以预编译为f, 从而解决性能问题,这正是witrix平台中tpl技术所采用的方案。

posted @ 2005-12-02 22:59 canonical 阅读(521) | 评论 (2)编辑 收藏

    AOP作为一种新的分解与合成技术,除了性能问题之外,仍有一些概念层面上的细节问题需要解决。最近Stoerzer的一篇论文AOP Considered harmful因为与Dijkstra的经典论文Go To Statement Considered Harmful  进行对比而引起了广泛的讨论。

    Dijkstra认为程序运行时的指令序列是我们最终想要的东西,而这一序列是运行时根据源代码的描述在时间轴上展开的(串行结构)。因为人们更容易理解 静态关系而不是随时间演化的过程,所以我们应该尽量缩小静态程序(spread out in text space)和动态过程(spread out in time)的逻辑差距,因而我们需要使它们能够在一个固定的坐标系统(coordinate system)下形成对应。对于包括条件和分支语句的串行程序,我们只需要源代码的行号(line number)即可确定一个单一位置。在循环的情况下,我们只需要增加一个额外的循环计数器(loop counter)即可保证可理解性。而对于子例程(procedure)调用,我们可以认为整个调用堆栈(call stack)也构成坐标系统的一部分。goto导致一种非结构化的控制流,因而破坏了这种理解上所必需的独立坐标系统。例如,如果一个循环中充满了自由的 goto调转(可能跳出循环又跳回),我们就很难确定循环变量的值到底是怎么增加的,除非我们在脑海中把源代码运行一遍!
  仿照Dijkstra的分析,Stoerzer指出AOP Advice虽然类似于procedure,但存在如下重要区别: 1. 与方法调用不同,advice执行位置在基础源代码中没有标识(obliviousness of application), advice有可能在任何位置插入并改变现场变量的值 2. pointcut可能依赖运行时变量值而无法静态计算得出(non-certainty of application)。
    第一点是由AOP技术的开放性造成的,但正如面向对象中的原则: open to extension but close to modification,我们需要遵循一些原则来避免破坏原有的结构。当然,AOP应用的场景可能确实只存在着某种弱可分性,advice需要深度依赖base code中的一些特性,可能应用类似模板(template)的技术会在一定程度上缓解encapsulation breaking. AOP的开放性造成的更严重的问题是pointcut在演化过程中的不确定性。只有在拥有全局知识的情况下才能确认pointcut的结果正是我们所期望的。特别是重构造成方法名改变之后,pointcut无法监测这种变化。当base code修改之后,我们可能没有意识到缺省影响到很多的aspect, 即完全理解base code变得非常困难。这种困境有一部分的原因是方法名同时作为调用标记和pointcut标记,责任过重造成的。参考一下css的选择符
   selector { property: value }

              \_declaration_/
   \___________ rule _________/
css可以通过选择符应用,也可以通过指定标签的class属性来应用,选择符所依靠的选择特征也可以不仅仅是标签名而包含属性名等。Java最近增加了与dotNet类似的meta attribute的支持,pointcut所依赖的元数据与方法名分离之后应该可以提高pointcut的稳定性。
  关于第二点,实际上OOP中的Dynamic Dispatch在某种程度上也是需要动态计算决定的,但因为接口具有明确的概念含义(an overriding method should only expect less and provide more, by maintaining all invariants),我们可以在更高的层次上理解代码,而不需要具体到特定的实现。AOP目前可能是缺乏一些指导性的设计原则。
  相对OOP而言,AOP影响到大范围内的对象及系统的一些整体特性,因而更加需要工具的支持。

posted @ 2005-12-02 22:50 canonical 阅读(488) | 评论 (1)编辑 收藏

     接口(interface)总对应于某种明确的概念,它并不简简单单的等价于其成员函数的集合。有的接口如java.io.Serializable甚至没有任何成员函数。接口最重要的就是名, 是对概念的甄别。接口发布出去之后才能够被实现。当我们使用某个接口的时候,即使我们只用到其中部分函数,我们也必须负担整个概念。虽说"有名,万物之母", 并不是任何时候我们都需要名的。我们会说,就要那个,蓝色的,这么高,... blabla, 对,就是这个(this)。模板(template)弱化了类型系统,它对系统的约束直接作用在细节行为上,降低了明确建模的需求,不需要概念的分解,合并,比接口更加灵活。但模板并不是任何时候都比接口更好。想象一下,我们拿着一张采购单,上面写着需要某个物品,前面有个尖,后面有个帽,细长形,大概这么长,这么粗,上面有螺纹,螺距这么大,。。。这是...三号螺钉?嗯,最近有一种新产品,塑料材质的,你要不要试试。
   模板与接口在某种程度上是互补的。

posted @ 2005-12-02 22:37 canonical 阅读(344) | 评论 (0)编辑 收藏