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

2005年12月12日

    据说"Less is more"是1961年宝姿品牌提出的设计理念,它开创了简约优雅的时尚风格。Unix系统的设计可以说也是这一设计理念的最佳体现。使用Unix工具, 读Unix系统源码,我们时时都能体会到一种简约之美。而Microsoft发放出来的源代码一般都相当冗长,变量名也长,这一度让我很反感。为了简化C ++中的COM编程,我不得不专门写了一个封装框架。
    简约并不简单。例如,注释一般都是期望能够帮助理解的,应该是有益的。但注释太多就会干扰对程序的理解。这里关键就是能否提供有效的信息量。利用这一点,我们换一个角度看,多未必意味着复杂。例如,这样的一个函数调用
          StupidFunc(arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8)
    显然不如
          BetterFunc(userName:arg1, userMail:arg2, ...)
    非常紧凑的调用可能需要极端强度的注意力和思考力的投入,即要求我们付出的更多才能够理解。Perl这样的脚本是可以紧致到变态的一种语言。 Microsoft的自动化组件支持命名参数,则大大简化了OLE编程。与此类似,xml动态脚本中的函数调用也是比较容易理解的,甚至比java函数调 用更容易理解。
       <sendMail from="a@b.com" to="c@d.com" text="hello" />
    使用witrix平台中的SQL类写起SQL语句来虽然语句很长,但参数意义明显,并不容易出错。
    很多时候都存在着某个最适尺度,偏离了这个尺度,我们就会遇上各种各样的"More"。不要画蛇添足,也不要削足适履。

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

    在witrix平台中,validate.js提供了完整的客户端输入校验框架。其基本思想是为每个输入控件指定验证函数(validator属性),在提交Form的时候自动调用该验证函数即可。
<form action="test.jsp">
<input type="text" name="userName" validator="js.validate.checkNotEmpty(value,'用户名')" />
<input type="button" value="submit" onclick="js.validate.submitForm(this.form)" />
</form>
    witrix平台的一个基本设计原则是模块的独立性,不仅各个模块之间的耦合很少,我们还尽量避免使用配置文件。与struts等web框架不同, witrix的输入校验不依赖于外部配置文件,可以完全独立的使用。虽然jsplet框架也提供了服务器端校验的支持,但在实际使用过程中却很少使用。客 户端校验提供了更好的用户体验。而如果我们需要进一步确保业务逻辑的稳定性,例如避免用户伪造客户端url请求,数据校验需要在业务逻辑对象层进行而不应 是在解析用户请求的时候。针对每个form所写的配置文件有很多不方便的地方,例如witrix平台支持从数据库描述文件直接生成操作界面的快速开发,校 验规则在数据描述文件中指定,而同一个字段可能在多个界面中出现,如果针对Form写校验配置文件,就会出现冗余,而难以保证结构的同步。实际上,一个结 构在界面上表现一次,又在校验配置文件中表现一次,就必然会出现同步问题,解决的方法就是面向对象设计中的对象化,局部化,而不是一个个分离的处理层。

    很多客户端的校验框架使用的是一个万能的校验函数,通过参数不同来实现不同校验。例如
<input type="text" name="userName" validateType="required" />
这种方式的扩展性不好。正如面向对象设计中的通常做法,我们通过使用回调函数(虚拟函数)来实现可扩展的设计。

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

    守破离(Shu Ha Ri)是日本剑道(Kendo)的哲学。http://c2.com/cgi/wiki?ShuHaRi  (日本人很善于推销自己传统的思想,而中国的传统却似乎在盲目自卑和盲目自大两个极端之间徘徊)
    守是模仿(Imitate),遵循,是无我的过程。在日本的传统心性中,守的阶段需要完全开放心志,全盘接受导师的教导。此时应该学习唯一的真理,知道唯 一正确的方法,分清对与错。通过长期不辍的练习,将对规则的记忆固化在自己的身体中。初学者看似是自由的,但也是不明智的(unwisely),我们总是 倾向于采用错误的方式。
    beginners are very hard to fight... they don't do anything you expect them to do. They move freely, and randomly. Only by returning to very fundamental principles, can one uncover the faults (unwisdom) in their actions and defeat them. 简言之,就是没有结构。

    破是变(diverge),是自我意识逐渐增强,心智逐渐收缩的过程。Just 'winning' is not enough, you must win well. 这个阶段我们需要区分出好与坏而不仅仅是两分的对与错。
    离是返朴归真, 是忘我的过程。最终我们得到行为和思想上的自由(freedom)。离看似是随机的,但实际上是混沌的(chaos)。
    It's being good irrespective of whether you are right or wrong.看似打破常规,举手投足却都遵循着道(Tao),这是持续而自由的创造。
    传统上守破离这三个递进的阶段是在导师监护下完成的,导师决定你是否进入下一阶段。而在没有导师的情况下,我们需要增加一个步骤:检验(Test),即我 们需要缩短每个阶段的时间,对我们的修行成果进行检验,通过迭代循环来自我实现阶段跨越。(http://www.aikidofaq.com/essays/tin/shuhari.html 提到Test, 大概是这篇文章在agile社区流传的原因之一吧,呵呵)。
     我们无法跨越守的阶段。敏捷编程绝不意味着没有design pattern。没有良好的基本功,一切都是空谈。

   破,首先是破除权威。小的时候我们喜欢引用大师,喜欢谈论他们的轶事,现在也是引用,但却经常略带狡黠的歪曲他们的原意。大学以后应该少去阅读大部头的 书,读薄的书,并把书读薄。真正的思想并不是很多,当大量的细节都成为背景知识以后,我们需要进行思考的内容并不是很多。避免重复书中的原话,因为那是别 人的思想。广为涉猎,多做比较。换个角度看一看,或通过类比,尽力建立事物之间的关联,同中求异,异中求同。我以物理学的观点来看待软件,这是我采取的破 的方式。

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

witrix平台中的tpl模板技术是一种通用的xml动态标签技术,不仅可以用于文本生成,而且可以用于任何需要动态标签的地方,例如工作流引擎 的配置和执行脚本。tpl模板引擎采用的不是jsp tag的标准机制,而是重新设计并实现的。在开发的后期,因为jstl标准出现,我们对标签的命名作了一定的修改,以尽量符合标准的调用接口。tpl模板 语言完全符合xml规范,其标签定义都是完全独立开发的。在开发tpl的时候,我们甚至没有看到任何类似于c:forEach和c:if的标签设计。但是 我们发现,tpl的动态处理功能与jstl虽然命名不同,但是基本是等价的,所以修改是非常直接的过程。

FreeMarker是一种流 行的文本模板语言,其语法类似于xml tag,但是命名规则不同。这实在是一种令人费解的设计。有意思的是,我们发现tpl的功能集也包含了FreeMarker的功能集。这实际上表明了一件 事情,xml动态标签存在一些必然的功能需求,无论是jsp tag, FreeMarker还是tpl, 所不同的只是表现形式而已。但这种表现形式的差异却又造成了实际功能上的巨大差异。

tpl与FreeMarker具体对比如下。

宏定义
<#macro greet person>
<font size="+2">Hello ${person}</font>
</#macro>]]>

<c:lib namespace="test">
<greet demandArgs="person">
<font size="+2">Hello ${person}</font>
</greet>
</c:lib>

tpl具有更加强大的变量域控制手段,可以通过importVars参数来指定是否使用调用环境中的变量作为缺省参数。另一方面,tpl具有更加灵活的参数校验规则,可以通过demandArgs, otherArgs等参数来指定对自定义标签参数的校验规则。
调用宏
<@greet person="Fred" />
<test:greet person="Fred" />

嵌套内容
<#macro border>
<table border="4" cellspacing="0" cellpadding="4"><tr><td>
<#nested>
<#nested>
</tr></td></table>
</#macro>
<c:lib namespace="test">
<border type="bodyTag">
<table border="4" cellspacing="0" cellpadding="4"><tr><td>
<cp:compile src="${tagBody}" />
</tr></td></table>
</border>
</c:lib>

tpl的<cp:compile>指令在执行时可以指定xslt参数,从而在编译tagBody之前应用xslt变换。
复杂嵌套
与FreeMark一样,嵌套内容可以是复杂内容

<@border>
<ul>
<@do_thrice>
<li><@greet person="Joe"/>
/@do_thrice
</ul>
/@border
<test:border>
<ul>
<test:do_thrice>
<li><test:greet person="Joe" /></li>
</test:do_thrice>
</ul>
</test:border>

导入库
<#import "/lib/my_test.ftl" as my>
<c:lib src="/lib/my_test.ftl" namespace="my" />

创建或替代变量
<#assign mail="jsmith@other.com" />
<c:set var="mail" value="jsmith@other.com" default="xx"/>

判断
<#if animals.python.price < animals.elephant.price>
Pythons are cheaper than elephants today.
</#if>
<c:if test="${lt(animals.python.price,animals.elephant.price)}">
Pythons are cheaper than elephants today.
</c:if>

tpl因为是xml语法,算术操作符<和>必须转义后才能使用,使用起来很不方便,因而最终决定tpl不支持操作符,通过lt(), gt()等函数来实现功能。
循环
<#list animals as being>
<tr><td>${being.name}<td>${being.price} Euros
</#list>
<c:forEach var="being" items="${animals}" >
<tr><td>${being.name}<td>${being.price} Euros
</c:forEach>

tpl提供<c:tile>等多种循环方式
include指令
<#include "/copyright_footer.html">
<c:include src="/copyright_footer.html" />

tpl强大的模板功能加上jsplet框架面向对象的能力,使得我们可以轻易的封装复杂的界面组件。而且这种封装能力还不需要Tapestry那种复杂的配置文件。tpl对portal应用的支持也是一个自然的发展过程。

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

    http://www.ps.uni-sb.de/~duchier/python/continuations.html
    A continuation is a procedure that takes the value of the current expression and computes the rest of the computation.

    Continuation是一种非常古老的程序结构,关于它的理论分析可谓渊源流长,参见http://library.readscheme.org/page6.html
    continuation简单的说起来就是entire default future of a computation, 即对程序"接下来要做的事情"所进行的一种建模. 这一概念在理论上当然存在着巨大的价值, 至少它使得我们有一种手段把程序未来的运行过程明确的表达出来(给它取了个名字), 从而有可能对之作进一步的分析. continuation是对未来的完整描述, 这对于理论分析而言是有很多方便之处的, 正如统计学中最常见的分析工具是分布函数而不是密度函数一样. 实际上任何程序都可以通过所谓的CPS(Continuation Passing Style)变换而转换为使用continuation结构, 例如
    int foo(int x){
        return x+1;
    }
     ==>
    void foo(int x,Continuation c){
        c.continueWith(x+1);
    }   
    
    使用continuation的函数不"返回"值,而是把值作为一个参数传递给continuation从而"继续"处理值. 在传统的软件理论中, 程序本身在运行期是固定不变的, 我们只需要记录下执行点(excution point)的信息(例如指针位置和堆栈内容)即足以完整的描述程序未来的运行情况, 因此continuation有时也被看作是"带参数的goto", 是goto语句的一种函数形式.
    在函数式语言中, continuation的引入是非常自然的过程, 考察如下函数调用
         h(g(k(arg)))
    根据函数的结合律, 我们可以定义复合函数 m = h(g(.)), 它自然的成为 k(arg)的continuation. 在理论上我们有可能利用泛函分析的一些技术实现对于continuation(复合函数)的化简, 但实践已经证明这是极为艰难的, 主要是我们的程序不可避免的要涉及到程序与数据的纠缠.
    在引入continuation概念之后, 程序运行的表述是非常简单的:
        continuation.proceed();

    针对串行程序,我们可以建立更加精细的运行模型。
        while(continuation.hasNextStep())
            continuation.proceedOneStep();
   
    只要以某种方式构造出一种continuation closure(这意味着我们能够通过单一变量来表示程序未来的运行结构), 我们就有可能在某个层面上以如上方式实现对程序的一种简洁的描述.
    如果我们的眼界开阔一些, 不拘泥于构造语言级别通用的continuation结构(这需要以抽象的方式定义并保存任意程序的完整运行状态), 而是考察"对程序未来运行的整体结构进行建模"这一更宽广的命题, 我们很快就能发现大量对于continuation概念的应用. 例如实现AOP(Aspect Oriented Programming)的interceptor时所经常使用的MethodInvocation对象.
        class MyInterceptor implements MethodInterceptor{
            public Object invoke(MethodInvocation invocation){
                doSomeThingBeforeRawMethodCall();
                return invocation.proceed();
            }
        }
    
     在网络编程中, 一种常用的设计模式是Observer模式, 即注册监听器(listener)来处理接收到的网络指令. 在一些比较复杂的网络协议中, 网络指令之间往往存在一定的关联, 我们可以通过建立一个庞大的有限自动机来描述所有指令之间的关联规则, 也可以采用如下方式动态的实现对于监听器的调整.
        class ACommandListener{
            public void onEvent(Event event, FutureListeners futureListeners){
                handleEvent(event);
                futureListeners.clear();
                futureListeners.add("BCommand", new BCommandListener());
                futureListeners.add("CCommand", new CCommandListener());
            }
        }
    这种方式可以看作是对程序未来运行结构的一种动态调整. 实际上沿着这种方式深入下去, 我们甚至可以建立一种完整的动态工作流(workflow)机制.

     最近struts和webwork步cocoon和rife的后尘, 相继引入了对web continuation的支持, 在后台程序中实现了对于page flow的完整描述, 这无疑是一些非常有趣的工作. 例如现在我们可以编写
        void onRequest(){
            funcA();
            Input input = sendPageAndWait("collectionInfoFromUser.jsp");
            handleInput(input);
        }
     在调用sendPageAndWait的时候, web框架会保存当前函数调用的continuation, 向用户返回页面collectionInfoFromUser.jsp, 等待用户提交表单之后, web框架重新激活我们所保存的continuation, 继续执行我们的函数. 这种做法与系统调用和线程调度等机制是非常类似的.  
     有些人认为这种基于continuation的方式可以自然的解决在session中保存并清理变量的问题, 这显然是一种大材小用的做法, 而且事实上使用一种通用的continuation 实现很有可能在无意中保存了过多的临时变量, 从而对系统性能造成极大的损害. 有趣的是, 在Mach3.0中对系统线程所作的一项改进即称为continuation, 其动因恰在于避免保留线程堆栈,希望使用完全无状态的continuation函数.(参见Uresh Vahalia的经典著作"UNIX Internals" http://www.china-pub.com/computers/common/info.asp?id=12731).
    在传统的系统调用实现中
    syscall_l(argl)
    {
        ...
        thread_block();
        f2(arg);
        return;
    }
   
    f2(arg){
        ...
        return;
    }

    thread_block()函数会阻塞住当前系统调用过程, 并自动保存所有堆栈变量, 等待内核重新把控制权返回给调用函数. 在使用continuation函数的方式中, 我们需要显式的存取状态变量,
    syscall_1(arg1)
    {
        ...
        save arg1 and any other state information;
        thread_block(f2);  // thread_block(void * (contiuationFunc));
        /* not reached */
    }
   
    f2()
    {
        restore argl and any other state information;
        ...
        thread_syscall_return(status);
    }
    在这种方式中thread_block()并不返回到调用者。线程恢复执行时,内核把控制权传递给f2(). 函数thread_syscall_return()用来从系统调用返回到用户级。"整个过程对用户是透明的,用户所看到的只是从系统调用一个同步返回 ". 在Linux系统内核中所使用的bottom_half机制也是基于类似的原理.

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