﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-canonical-随笔分类-软件开发</title><link>http://www.blogjava.net/canonical/category/4851.html</link><description /><language>zh-cn</language><lastBuildDate>Sun, 25 Dec 2011 22:51:43 GMT</lastBuildDate><pubDate>Sun, 25 Dec 2011 22:51:43 GMT</pubDate><ttl>60</ttl><item><title>jQuery中的编程范式</title><link>http://www.blogjava.net/canonical/archive/2011/12/25/367146.html</link><dc:creator>canonical</dc:creator><author>canonical</author><pubDate>Sun, 25 Dec 2011 13:23:00 GMT</pubDate><guid>http://www.blogjava.net/canonical/archive/2011/12/25/367146.html</guid><wfw:comment>http://www.blogjava.net/canonical/comments/367146.html</wfw:comment><comments>http://www.blogjava.net/canonical/archive/2011/12/25/367146.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/canonical/comments/commentRss/367146.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/canonical/services/trackbacks/367146.html</trackback:ping><description><![CDATA[<div>&nbsp; 浏览器前端编程的面貌自2005年以来已经发生了深刻的变化，这并不简单的意味着出现了大量功能丰富的基础库，使得我们可以更加方便的编写业务代码，更重要的是我们看待前端技术的观念发生了重大转变，明确意识到了如何以前端特有的方式释放程序员的生产力。本文将结合jQuery源码的实现原理，对javascript中涌现出的编程范式和常用技巧作一简单介绍。<br />&nbsp; <br />1. AJAX: 状态驻留，异步更新<br />&nbsp; 首先来看一点历史。<br />A. 1995年Netscape公司的Brendan Eich开发了javacript语言，这是一种动态(dynamic)、弱类型(weakly typed)、基于原型(prototype-based)的脚本语言。<br />B. 1999年微软IE5发布，其中包含了XMLHTTP ActiveX控件。<br />C. 2001年微软IE6发布，部分支持DOM level 1和CSS 2标准。<br />D. 2002年Douglas Crockford发明JSON格式。<br />至此，可以说Web2.0所依赖的技术元素已经基本成形，但是并没有立刻在整个业界产生重大的影响。尽管一些&#8220;页面异步局部刷新&#8221;的技巧在程序员中间秘密的流传，甚至催生了bindows这样庞大臃肿的类库，但总的来说，前端被看作是贫瘠而又肮脏的沼泽地，只有后台技术才是王道。到底还缺少些什么呢？<br />&nbsp; 当我们站在今天的角度去回顾2005年之前的js代码，包括那些当时的牛人所写的代码，可以明显的感受到它们在程序控制力上的孱弱。并不是说2005年之前的js技术本身存在问题，只是它们在概念层面上是一盘散沙，缺乏统一的观念，或者说缺少自己独特的风格, 自己的灵魂。当时大多数的人，大多数的技术都试图在模拟传统的面向对象语言，利用传统的面向对象技术，去实现传统的GUI模型的仿制品。<br />&nbsp; 2005年是变革的一年，也是创造概念的一年。伴随着Google一系列让人耳目一新的交互式应用的发布,Jesse James Garrett的一篇文章《Ajax: A New Approach to Web Applications》被广为传播。Ajax这一前端特有的概念迅速将众多分散的实践统一在同一口号之下，引发了Web编程范式的转换。所谓名不正则言不顺，这下无名群众可找到组织了。在未有Ajax之前，人们早已认识到了B/S架构的本质特征在于浏览器和服务器的状态空间是分离的，但是一般的解决方案都是隐藏这一区分，将前台状态同步到后台，由后台统一进行逻辑处理，例如ASP.NET。因为缺乏成熟的设计模式支持前台状态驻留，在换页的时候，已经装载的js对象将被迫被丢弃，这样谁还能指望它去完成什么复杂的工作吗？<br />&nbsp; Ajax明确提出界面是局部刷新的，前台驻留了状态，这就促成了一种需要：需要js对象在前台存在更长的时间。这也就意味着需要将这些对象和功能有效的管理起来，意味着更复杂的代码组织技术，意味着对模块化，对公共代码基的渴求。<br />&nbsp; jQuery现有的代码中真正与Ajax相关（使用XMLHTTP控件异步访问后台返回数据）的部分其实很少，但是如果没有Ajax, jQuery作为公共代码基也就缺乏存在的理由。<br /><br />2. 模块化：管理名字空间<br />&nbsp; 当大量的代码产生出来以后，我们所需要的最基础的概念就是模块化，也就是对工作进行分解和复用。工作得以分解的关键在于各人独立工作的成果可以集成在一起。这意味着各个模块必须基于一致的底层概念，可以实现交互，也就是说应该基于一套公共代码基，屏蔽底层浏览器的不一致性，并实现统一的抽象层，例如统一的事件管理机制等。比统一代码基更重要的是，各个模块之间必须没有名字冲突。否则，即使两个模块之间没有任何交互，也无法共同工作。<br />&nbsp; jQuery目前鼓吹的主要卖点之一就是对名字空间的良好控制。这甚至比提供更多更完善的功能点都重要的多。良好的模块化允许我们复用任何来源的代码，所有人的工作得以积累叠加。而功能实现仅仅是一时的工作量的问题。jQuery使用module pattern的一个变种来减少对全局名字空间的影响,仅仅在window对象上增加了一个jQuery对象(也就是$函数)。<br />&nbsp;&nbsp; 所谓的module pattern代码如下，它的关键是利用匿名函数限制临时变量的作用域。<br />&nbsp; var feature =(function() {<br /><br />// 私有变量和函数<br />var privateThing = 'secret',<br />&nbsp;&nbsp;&nbsp; publicThing = 'not secret',<br /><br />&nbsp;&nbsp;&nbsp; changePrivateThing = function() {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; privateThing = 'super secret';<br />&nbsp;&nbsp;&nbsp; },<br /><br />&nbsp;&nbsp;&nbsp; sayPrivateThing = function() {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; console.log(privateThing);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; changePrivateThing();<br />&nbsp;&nbsp;&nbsp; };<br /><br />// 返回对外公开的API<br />return {<br />&nbsp;&nbsp;&nbsp; publicThing : publicThing,<br />&nbsp;&nbsp;&nbsp; sayPrivateThing :&nbsp; sayPrivateThing<br />}<br />})();<br />&nbsp; js本身缺乏包结构，不过经过多年的尝试之后业内已经逐渐统一了对包加载的认识，形成了RequireJs库这样得到一定共识的解决方案。jQuery可以与RequireJS库良好的集成在一起, 实现更完善的模块依赖管理。http://requirejs.org/docs/jquery.html<br />&nbsp; <br />&nbsp; require(["jquery", "jquery.my"], function() {<br />&nbsp;&nbsp;&nbsp; //当jquery.js和jquery.my.js都成功装载之后执行<br />&nbsp;&nbsp;&nbsp; $(function(){<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $('#my').myFunc();<br />&nbsp;&nbsp;&nbsp; });<br />&nbsp; });<br />&nbsp; <br />&nbsp; 通过以下函数调用来定义模块my/shirt, 它依赖于my/cart和my/inventory模块，<br />&nbsp; require.def("my/shirt",<br />&nbsp;&nbsp;&nbsp; ["my/cart", "my/inventory"],<br />&nbsp;&nbsp;&nbsp; function(cart, inventory) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 这里使用module pattern来返回my/shirt模块对外暴露的API<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; color: "blue",<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; size: "large"<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; addToCart: function() {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // decrement是my/inventory对外暴露的API<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; inventory.decrement(this);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cart.add(this);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; }<br />&nbsp; );<br /><br />3. 神奇的$：对象提升<br />&nbsp; 当你第一眼看到$函数的时候，你想到了什么？传统的编程理论总是告诉我们函数命名应该准确，应该清晰无误的表达作者的意图，甚至声称长名字要优于短名字，因为减少了出现歧义的可能性。但是，$是什么？乱码？它所传递的信息实在是太隐晦，太暧昧了。$是由prototype.js库发明的，它真的是一个神奇的函数，因为它可以将一个原始的DOM节点提升(enhance)为一个具有复杂行为的对象。在prototype.js最初的实现中，$函数的定义为<br />&nbsp; var $ = function (id) {<br />&nbsp;&nbsp;&nbsp; return "string" == typeof id ? document.getElementById(id) : id;<br />&nbsp; };<br />&nbsp; 这基本对应于如下公式<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; e = $(id)<br />&nbsp; 这绝不仅仅是提供了一个聪明的函数名称缩写，更重要的是在概念层面上建立了文本id与DOM element之间的一一对应。在未有$之前，id与对应的element之间的距离十分遥远，一般要将element缓存到变量中，例如<br />&nbsp; var ea = docuement.getElementById('a');<br />&nbsp; var eb = docuement.getElementById('b');<br />&nbsp; ea.style....<br />但是使用$之后，却随处可见如下的写法<br />&nbsp; $('header_'+id).style...<br />&nbsp; $('body_'+id)....<br />id与element之间的距离似乎被消除了，可以非常紧密的交织在一起。<br />&nbsp; prototype.js后来扩展了$的含义，<br />&nbsp; function $() {<br />&nbsp;&nbsp;&nbsp; var elements = new Array();<br />&nbsp;&nbsp; &nbsp;<br />&nbsp;&nbsp;&nbsp; for (var i = 0; i &lt; arguments.length; i++) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var element = arguments[i];<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (typeof element == 'string')<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; element = document.getElementById(element);<br />&nbsp;&nbsp; &nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (arguments.length == 1)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return element;<br />&nbsp;&nbsp; &nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; elements.push(element);<br />&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp; &nbsp;<br />&nbsp;&nbsp;&nbsp; return elements;<br />&nbsp; }<br />&nbsp; 这对应于公式<br />&nbsp;&nbsp;&nbsp; [e,e] = $(id,id)<br />&nbsp; 很遗憾，这一步prototype.js走偏了，这一做法很少有实用的价值。<br />&nbsp; 真正将$发扬光大的是jQuery, 它的$对应于公式<br />&nbsp;&nbsp;&nbsp; [o] = $(selector)<br />&nbsp; 这里有三个增强<br />&nbsp; A. selector不再是单一的节点定位符，而是复杂的集合选择符<br />&nbsp; B. 返回的元素不是原始的DOM节点，而是经过jQuery进一步增强的具有丰富行为的对象，可以启动复杂的函数调用链。<br />&nbsp; C. $返回的包装对象被造型为数组形式，将集合操作自然的整合到调用链中。<br /><br />&nbsp; 当然,以上仅仅是对神奇的$的一个过分简化的描述,它的实际功能要复杂得多. 特别是有一个非常常用的直接构造功能. <br />&nbsp;&nbsp; $("&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;...&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;")....<br />&nbsp; jQuery将根据传入的html文本直接构造出一系列的DOM节点,并将其包装为jQuery对象. 这在某种程度上可以看作是对selector的扩展: html内容描述本身就是一种唯一指定. <br />&nbsp; $(function{})这一功能就实在是让人有些无语了, 它表示当document.ready的时候调用此回调函数。真的，$是一个神奇的函数, 有任何问题，请$一下。<br />&nbsp; 总结起来, $是从普通的DOM和文本描述世界到具有丰富对象行为的jQuery世界的跃迁通道。跨过了这道门，就来到了理想国。<br />&nbsp; &nbsp;<br />4. 无定形的参数：专注表达而不是约束<br />&nbsp; 弱类型语言既然头上顶着个"弱"字, 总难免让人有些先天不足的感觉. 在程序中缺乏类型约束, 是否真的是一种重大的缺憾? 在传统的强类型语言中, 函数参数的类型,个数等都是由编译器负责检查的约束条件, 但这些约束仍然是远远不够的. 一般应用程序中为了加强约束, 总会增加大量防御性代码, 例如在C++中我们常用ASSERT, 而在java中也经常需要判断参数值的范围<br />&nbsp;&nbsp;&nbsp; if (index &lt; 0 || index &gt;= size)<br />&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; throw new IndexOutOfBoundsException(<br />&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; "Index: "+index+", Size: "+size);&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;<br />&nbsp; 很显然, 这些代码将导致程序中存在大量无功能的执行路径, 即我们做了大量判断, 代码执行到某个点, 系统抛出异常, 大喊此路不通. 如果我们换一个思路, 既然已经做了某种判断,能否利用这些判断的结果来做些什么呢? javascript是一种弱类型的语言,它是无法自动约束参数类型的, 那如果顺势而行,进一步弱化参数的形态, 将"弱"推进到一种极致, 在弱无可弱的时候, weak会不会成为标志性的特点? <br />&nbsp; 看一下jQuery中的事件绑定函数bind, <br />&nbsp;&nbsp; A. 一次绑定一个事件 $("#my").bind("mouseover", function(){});<br />&nbsp;&nbsp; B. 一次绑定多个事件 $("#my").bind("mouseover mouseout",function(){})<br />&nbsp;&nbsp; C. 换一个形式, 同样绑定多个事件 <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $("#my").bind({mouseover:function(){}, mouseout:function(){});<br />&nbsp;&nbsp; D. 想给事件监听器传点参数<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $('#my').bind('click', {foo: "xxxx"}, function(event) { event.data.foo..})<br />&nbsp;&nbsp; E. 想给事件监听器分个组<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $("#my").bind("click.myGroup&#8243;, function(){});<br />&nbsp;&nbsp; F. 这个函数为什么还没有疯掉???<br />&nbsp; &nbsp;<br />&nbsp;&nbsp; 就算是类型不确定, 在固定位置上的参数的意义总要是确定的吧? 退一万步来说, 就算是参数位置不重要了,函数本身的意义应该是确定的吧? 但这是什么? <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 取值 value = o.val(), 设置值 o.val(3)<br />&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;<br />&nbsp;&nbsp; 一个函数怎么可以这样过分, 怎么能根据传入参数的类型和个数不同而行为不同呢? 看不顺眼是不是? 可这就是俺们的价值观. 既然不能防止, 那就故意允许. 虽然形式多变, 却无一句废话. 缺少约束, 不妨碍表达(我不是出来吓人的). <br />&nbsp; &nbsp;<br />5. 链式操作: 线性化的逐步细化<br />&nbsp; jQuery早期最主要的卖点就是所谓的链式操作(chain). <br />&nbsp; <br />&nbsp; $('#content') // 找到content元素<br />&nbsp;&nbsp;&nbsp; .find('h3') // 选择所有后代h3节点<br />&nbsp;&nbsp;&nbsp; .eq(2)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 过滤集合, 保留第三个元素<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .html('改变第三个h3的文本')<br />&nbsp;&nbsp;&nbsp; .end()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 返回上一级的h3集合<br />&nbsp;&nbsp;&nbsp; .eq(0)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .html('改变第一个h3的文本');<br /><br />在一般的命令式语言中, 我们总需要在重重嵌套循环中过滤数据, 实际操作数据的代码与定位数据的代码纠缠在一起. 而jQuery采用先构造集合然后再应用函数于集合的方式实现两种逻辑的解耦, 实现嵌套结构的线性化. 实际上, 我们并不需要借助过程化的思想就可以很直观的理解一个集合, 例如 $('div.my input:checked')可以看作是一种直接的描述,而不是对过程行为的跟踪. <br />&nbsp;&nbsp; 循环意味着我们的思维处于一种反复回绕的状态, 而线性化之后则沿着一个方向直线前进, 极大减轻了思维负担, 提高了代码的可组合性. 为了减少调用链的中断, jQuery发明了一个绝妙的主意: jQuery包装对象本身类似数组（集合）. 集合可以映射到新的集合， 集合可以限制到自己的子集合，调用的发起者是集合，返回结果也是集合，集合可以发生结构上的某种变化但它还是集合, 集合是某种概念上的不动点，这是从函数式语言中吸取的设计思想。集合操作是太常见的操作, 在java中我们很容易发现大量所谓的封装函数其实就是在封装一些集合遍历操作, 而在jQuery中集合操作因为太直白而不需要封装. <br />&nbsp;&nbsp; 链式调用意味着我们始终拥有一个&#8220;当前&#8221;对象，所有的操作都是针对这一当前对象进行。这对应于如下公式<br />&nbsp;&nbsp;&nbsp;&nbsp; x += dx<br />调用链的每一步都是对当前对象的增量描述，是针对最终目标的逐步细化过程。Witrix平台中对这一思想也有着广泛的应用。特别是为了实现平台机制与业务代码的融合，平台会提供对象（容器）的缺省内容，而业务代码可以在此基础上进行逐步细化的修正，包括取消缺省的设置等。<br />&nbsp; 话说回来, 虽然表面上jQuery的链式调用很简单, 内部实现的时候却必须自己多写一层循环, 因为编译器并不知道"自动应用于集合中每个元素"这回事. <br />&nbsp; $.fn['someFunc'] = function(){<br />&nbsp;&nbsp;&nbsp; return this.each(function(){<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; jQuery.someFunc(this,...);<br />&nbsp;&nbsp;&nbsp; }<br />&nbsp; }<br />&nbsp; <br />6. data: 统一数据管理<br />&nbsp; 作为一个js库，它必须解决的一个大问题就是js对象与DOM节点之间的状态关联与协同管理问题。有些js库选择以js对象为主，在js对象的成员变量中保存DOM节点指针，访问时总是以js对象为入口点，通过js函数间接操作DOM对象。在这种封装下，DOM节点其实只是作为界面展现的一种底层&#8220;汇编&#8221;而已。jQuery的选择与Witrix平台类似，都是以HTML自身结构为基础，通过js增强(enhance)DOM节点的功能，将它提升为一个具有复杂行为的扩展对象。这里的思想是非侵入式设计(non-intrusive)和优雅退化机制(graceful degradation)。语义结构在基础的HTML层面是完整的，js的作用是增强了交互行为，控制了展现形式。<br />&nbsp; 如果每次我们都通过$('#my')的方式来访问相应的包装对象，那么一些需要长期保持的状态变量保存在什么地方呢？jQuery提供了一个统一的全局数据管理机制。<br />&nbsp; 获取数据 $('#my').data('myAttr')&nbsp;&nbsp; 设置数据 $('#my').data('myAttr',3);<br />这一机制自然融合了对HTML5的data属性的处理<br />&nbsp;&nbsp; &lt;input id="my" data-my-attr="4" ... /&gt;<br />&nbsp;通过 $('#my').data('myAttr')将可以读取到HTML中设置的数据。<br />&nbsp;<br />&nbsp;第一次访问data时，jQuery将为DOM节点分配一个唯一的uuid, 然后设置在DOM节点的一个特定的expando属性上, jQuery保证这个uuid在本页面中不重复。<br />&nbsp;&nbsp; elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];<br />&nbsp;以上代码可以同时处理DOM节点和纯js对象的情况。如果是js对象，则data直接放置在js对象自身中，而如果是DOM节点，则通过cache统一管理。<br />&nbsp;因为所有的数据都是通过data机制统一管理的，特别是包括所有事件监听函数(data.events)，因此jQuery可以安全的实现资源管理。在clone节点的时候，可以自动clone其相关的事件监听函数。而当DOM节点的内容被替换或者DOM节点被销毁的时候，jQuery也可以自动解除事件监听函数, 并安全的释放相关的js数据。<br />&nbsp;<br />7. event：统一事件模型<br />&nbsp; "事件沿着对象树传播"这一图景是面向对象界面编程模型的精髓所在。对象的复合构成对界面结构的一个稳定的描述，事件不断在对象树的某个节点发生，并通过冒泡机制向上传播。对象树很自然的成为一个控制结构，我们可以在父节点上监听所有子节点上的事件，而不用明确与每一个子节点建立关联。<br />&nbsp; jQuery除了为不同浏览器的事件模型建立了统一抽象之外,主要做了如下增强:<br />&nbsp; A. 增加了自定制事件(custom)机制. 事件的传播机制与事件内容本身原则上是无关的, 因此自定制事件完全可以和浏览器内置事件通过同一条处理路径, 采用同样的监听方式. 使用自定制事件可以增强代码的内聚性, 减少代码耦合. 例如如果没有自定制事件, 关联代码往往需要直接操作相关的对象<br />&nbsp; $('.switch, .clapper').click(function() {<br />&nbsp;&nbsp;&nbsp; var $light = $(this).parent().find('.lightbulb');<br />&nbsp;&nbsp;&nbsp; if ($light.hasClass('on')) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $light.removeClass('on').addClass('off');<br />&nbsp;&nbsp;&nbsp; } else {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $light.removeClass('off').addClass('on');<br />&nbsp;&nbsp;&nbsp; }<br />&nbsp; });<br />而如果使用自定制事件,则表达的语义更加内敛明确,<br />&nbsp; $('.switch, .clapper').click(function() {<br />&nbsp;&nbsp;&nbsp; $(this).parent().find('.lightbulb').trigger('changeState');<br />&nbsp; });<br />&nbsp; B. 增加了对动态创建节点的事件监听. bind函数只能将监听函数注册到已经存在的DOM节点上. 例如<br />&nbsp;&nbsp;&nbsp; $('li.trigger').bind('click',function(){}}<br />&nbsp; 如果调用bind之后,新建了另一个li节点,则该节点的click事件不会被监听. <br />&nbsp; jQuery的delegate机制可以将监听函数注册到父节点上, 子节点上触发的事件会根据selector被自动派发到相应的handlerFn上. 这样一来现在注册就可以监听未来创建的节点. <br />&nbsp;&nbsp;&nbsp; $('#myList').delegate('li.trigger', 'click', handlerFn);<br />&nbsp; 最近jQuery1.7中统一了bind, live和delegate机制, 天下一统, 只有on/off.<br />&nbsp;&nbsp;&nbsp; $('li.trigger&#8217;).on('click', handlerFn);&nbsp; // 相当于bind<br />&nbsp;&nbsp;&nbsp; $('#myList&#8217;).on('click', 'li.trigger', handlerFn);&nbsp; // 相当于delegate<br />&nbsp;&nbsp; &nbsp;<br />8. 动画队列：全局时钟协调<br />&nbsp; 抛开jQuery的实现不谈, 先考虑一下如果我们要实现界面上的动画效果, 到底需要做些什么? 比如我们希望将一个div的宽度在1秒钟之内从100px增加到200px. 很容易想见, 在一段时间内我们需要不时的去调整一下div的宽度, [同时]我们还需要执行其他代码. 与一般的函数调用不同的是, 发出动画指令之后, 我们不能期待立刻得到想要的结果, 而且我们不能原地等待结果的到来. 动画的复杂性就在于:一次性表达之后要在一段时间内执行,而且有多条逻辑上的执行路径要同时展开, 如何协调? <br />&nbsp; 伟大的艾萨克.牛顿爵士在《自然哲学的数学原理》中写道:"绝对的、真正的和数学的时间自身在流逝着". 所有的事件可以在时间轴上对齐, 这就是它们内在的协调性. 因此为了从步骤A1执行到A5, 同时将步骤B1执行到B5, 我们只需要在t1时刻执行[A1, B1], 在t2时刻执行[A2,B2], 依此类推. <br />&nbsp;&nbsp;&nbsp; t1 | t2 | t3 | t4 | t5 ...<br />&nbsp;&nbsp;&nbsp; A1 | A2 | A3 | A4 | A5 ...<br />&nbsp;&nbsp;&nbsp; B1 | B2 | B3 | B4 | B5 ...<br />&nbsp; 具体的一种实现形式可以是 <br />&nbsp; A. 对每个动画, 将其分装为一个Animation对象, 内部分成多个步骤.<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; animation = new Animation(div,"width",100,200,1000,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 负责步骤切分的插值函数,动画执行完毕时的回调函数);<br />&nbsp; B. 在全局管理器中注册动画对象<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; timerFuncs.add(animation);<br />&nbsp; C. 在全局时钟的每一个触发时刻, 将每个注册的执行序列推进一步, 如果已经结束, 则从全局管理器中删除.<br />&nbsp;&nbsp;&nbsp;&nbsp; for each animation in timerFuncs<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(!animation.doOneStep())<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; timerFuncs.remove(animation)<br /><br />&nbsp; 解决了原理问题,再来看看表达问题, 怎样设计接口函数才能够以最紧凑形式表达我们的意图? 我们经常需要面临的实际问题:<br />&nbsp; A. 有多个元素要执行类似的动画<br />&nbsp; B. 每个元素有多个属性要同时变化<br />&nbsp; C. 执行完一个动画之后开始另一个动画<br />jQuery对这些问题的解答可以说是榨尽了js语法表达力的最后一点剩余价值. <br />&nbsp;&nbsp; $('input')<br />&nbsp;&nbsp;&nbsp;&nbsp; .animate({left:'+=200px',top:'300'},2000)<br />&nbsp;&nbsp;&nbsp;&nbsp; .animate({left:'-=200px',top:20},1000)<br />&nbsp;&nbsp;&nbsp;&nbsp; .queue(function(){<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 这里dequeue将首先执行队列中的后一个函数,因此alert("y")<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $(this).dequeue();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; alert('x');<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; })<br />&nbsp;&nbsp;&nbsp;&nbsp; .queue(function(){<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; alert("y");<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 如果不主动dequeue, 队列执行就中断了,不会自动继续下去.<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $(this).dequeue();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; });<br /><br />&nbsp; A. 利用jQuery内置的selector机制自然表达对一个集合的处理.<br />&nbsp; B. 使用Map表达多个属性变化<br />&nbsp; C. 利用微格式表达领域特定的差量概念. '+=200px'表示在现有值的基础上增加200px<br />&nbsp; D. 利用函数调用的顺序自动定义animation执行的顺序: 在后面追加到执行队列中的动画自然要等前面的动画完全执行完毕之后再启动.<br />&nbsp; &nbsp;<br />&nbsp; jQuery动画队列的实现细节大概如下所示,<br />&nbsp;&nbsp; A. animate函数实际是调用queue(function(){执行结束时需要调用dequeue,否则不会驱动下一个方法})<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; queue函数执行时, 如果是fx队列, 并且当前没有正在运行动画(如果连续调用两次animate,第二次的执行函数将在队列中等待),则会自动触发dequeue操作, 驱动队列运行.<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果是fx队列, dequeue的时候会自动在队列顶端加入"inprogress"字符串,表示将要执行的是动画.<br />&nbsp;&nbsp; B. 针对每一个属性，创建一个jQuery.fx对象。然后调用fx.custom函数(相当于start)来启动动画。<br />&nbsp;&nbsp; C. custom函数中将fx.step函数注册到全局的timerFuncs中，然后试图启动一个全局的timer.<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; timerId = setInterval( fx.tick, fx.interval );<br />&nbsp;&nbsp; D. 静态的tick函数中将依次调用各个fx的step函数。step函数中通过easing计算属性的当前值，然后调用fx的update来更新属性。<br />&nbsp;&nbsp; E. fx的step函数中判断如果所有属性变化都已完成,则调用dequeue来驱动下一个方法。<br /><br />&nbsp; 很有意思的是, jQuery的实现代码中明显有很多是接力触发代码: 如果需要执行下一个动画就取出执行, 如果需要启动timer就启动timer等. 这是因为js程序是单线程的,真正的执行路径只有一条,为了保证执行线索不中断, 函数们不得不互相帮助一下. 可以想见, 如果程序内部具有多个执行引擎, 甚至无限多的执行引擎, 那么程序的面貌就会发生本质性的改变. 而在这种情形下, 递归相对于循环而言会成为更自然的描述. <br />&nbsp; <br />9. promise模式：因果关系的识别<br />&nbsp; 现实中,总有那么多时间线在独立的演化着, 人与物在时空中交错,却没有发生因果. 软件中, 函数们在源代码中排着队, 难免会产生一些疑问, 凭什么排在前面的要先执行? 难道没有它就没有我? 让全宇宙喊着1,2,3齐步前进, 从上帝的角度看,大概是管理难度过大了, 于是便有了相对论. 如果相互之间没有交换信息, 没有产生相互依赖, 那么在某个坐标系中顺序发生的事件, 在另外一个坐标系中看来, 就可能是颠倒顺序的. 程序员依葫芦画瓢, 便发明了promise模式. <br />&nbsp; promise与future模式基本上是一回事,我们先来看一下java中熟悉的future模式.<br />&nbsp; futureResult = doSomething();<br />&nbsp; ...<br />&nbsp; realResult = futureResult.get();<br />&nbsp; 发出函数调用仅仅意味着一件事情发生过, 并不必然意味着调用者需要了解事情最终的结果. 函数立刻返回的只是一个将在未来兑现的承诺(Future类型), 实际上也就是某种句柄. 句柄被传来传去, 中间转手的代码对实际结果是什么,是否已经返回漠不关心. 直到一段代码需要依赖调用返回的结果, 因此它打开future, 查看了一下. 如果实际结果已经返回, 则future.get()立刻返回实际结果, 否则将会阻塞当前的执行路径, 直到结果返回为止. 此后再调用future.get()总是立刻返回, 因为因果关系已经被建立, [结果返回]这一事件必然在此之前发生, 不会再发生变化. <br />&nbsp; future模式一般是外部对象主动查看future的返回值, 而promise模式则是由外部对象在promise上注册回调函数. <br />&nbsp; function getData(){<br />&nbsp;&nbsp; return $.get('/foo/').done(function(){<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; console.log('Fires after the AJAX request succeeds');<br />&nbsp;&nbsp; }).fail(function(){<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; console.log('Fires after the AJAX request fails');<br />&nbsp;&nbsp; });<br />&nbsp; }<br />&nbsp;<br />&nbsp; function showDiv(){<br />&nbsp;&nbsp;&nbsp; var dfd = $.Deferred();<br />&nbsp;&nbsp;&nbsp; $('#foo').fadeIn( 1000, dfd.resolve );<br />&nbsp;&nbsp;&nbsp; return dfd.promise();<br />&nbsp; }<br />&nbsp;<br />&nbsp; $.when( getData(), showDiv() )<br />&nbsp;&nbsp;&nbsp; .then(function( ajaxResult, ignoreResultFromShowDiv ){<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; console.log('Fires after BOTH showDiv() AND the AJAX request succeed!');<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 'ajaxResult' is the server&#8217;s response<br />&nbsp;&nbsp;&nbsp; });<br />&nbsp; jQuery引入Deferred结构, 根据promise模式对ajax, queue, document.ready等进行了重构, 统一了异步执行机制. then(onDone, onFail)将向promise中追加回调函数, 如果调用成功完成(resolve), 则回调函数onDone将被执行, 而如果调用失败(reject), 则onFail将被执行. when可以等待在多个promise对象上. promise巧妙的地方是异步执行已经开始之后甚至已经结束之后,仍然可以注册回调函数<br />&nbsp; someObj.done(callback).sendRequest() vs. someObj.sendRequest().done(callback)<br />&nbsp;callback函数在发出异步调用之前注册或者在发出异步调用之后注册是完全等价的, 这揭示出程序表达永远不是完全精确的, 总存在着内在的变化维度. 如果能有效利用这一内在的可变性, 则可以极大提升并发程序的性能. <br />&nbsp;&nbsp; promise模式的具体实现很简单. jQuery._Deferred定义了一个函数队列，它的作用有以下几点：<br />&nbsp;&nbsp; A. 保存回调函数。<br />&nbsp;&nbsp; B. 在resolve或者reject的时刻把保存着的函数全部执行掉。<br />&nbsp;&nbsp; C. 已经执行之后, 再增加的函数会被立刻执行。<br />&nbsp; <br />&nbsp;&nbsp; 一些专门面向分布式计算或者并行计算的语言会在语言级别内置promise模式, 比如E语言. <br />&nbsp;&nbsp;&nbsp;&nbsp; def carPromise := carMaker &lt;- produce("Mercedes");<br />&nbsp;&nbsp;&nbsp;&nbsp; def temperaturePromise := carPromise &lt;- getEngineTemperature()<br />&nbsp;&nbsp;&nbsp;&nbsp; ...<br />&nbsp;&nbsp;&nbsp;&nbsp; when (temperaturePromise) -&gt; done(temperature) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; println(`The temperature of the car engine is: $temperature`)<br />&nbsp;&nbsp;&nbsp;&nbsp; } catch e {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; println(`Could not get engine temperature, error: $e`)<br />&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp; 在E语言中, &lt;-是eventually运算符, 表示最终会执行, 但不一定是现在. 而普通的car.moveTo(2,3)表示立刻执行得到结果. 编译器负责识别所有的promise依赖, 并自动实现调度. <br />&nbsp; <br />10. extend: 继承不是必须的<br />&nbsp; js是基于原型的语言, 并没有内置的继承机制, 这一直让很多深受传统面向对象教育的同学们耿耿于怀. 但继承一定是必须的吗? 它到底能够给我们带来什么? 最纯朴的回答是: 代码重用. 那么, 我们首先来分析一下继承作为代码重用手段的潜力. <br />&nbsp; 曾经有个概念叫做"多重继承", 它是继承概念的超级赛亚人版, 很遗憾后来被诊断为存在着先天缺陷, 以致于出现了一种对于继承概念的解读: 继承就是"is a"关系, 一个派生对象"is a"很多基类, 必然会出现精神分裂, 所以多重继承是不好的. <br />&nbsp;&nbsp; class A{ public: void f(){ f in A } }<br />&nbsp;&nbsp; class B{ public: void f(){ f in B } }<br />&nbsp;&nbsp; class D: public A, B{}<br />&nbsp;如果D类从A,B两个基类继承, 而A和B类中都实现了同一个函数f, 那么D类中的f到底是A中的f还是B中的f, 抑或是A中的f+B中的f呢? 这一困境的出现实际上源于D的基类A和B是并列关系, 它们满足交换律和结合律, 毕竟,在概念层面上我们可能难以认可两个任意概念之间会出现从属关系. 但如果我们放松一些概念层面的要求, 更多的从操作层面考虑一下代码重用问题, 可以简单的认为B在A的基础上进行操作, 那么就可以得到一个线性化的结果. 也就是说, 放弃A和B之间的交换律只保留结合律, extends A, B 与 extends B,A 会是两个不同的结果, 不再存在诠释上的二义性. scala语言中的所谓trait(特性)机制实际上采用的就是这一策略. <br />&nbsp; 面向对象技术发明很久之后, 出现了所谓的面向方面编程(AOP), 它与OOP不同, 是代码结构空间中的定位与修改技术. AOP的眼中只有类与方法, 不知道什么叫做意义. AOP也提供了一种类似多重继承的代码重用手段, 那就是mixin. 对象被看作是可以被打开,然后任意修改的Map, 一组成员变量与方法就被直接注射到对象体内, 直接改变了它的行为. <br />&nbsp; prototype.js库引入了extend函数,<br />&nbsp; Object.extend = function(destination, source) {<br />&nbsp;&nbsp;&nbsp; for (var property in source) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; destination[property] = source[property];<br />&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; return destination;<br />&nbsp; }<br />&nbsp; 就是Map之间的一个覆盖运算, 但很管用, 在jQuery库中也得到了延用. 这个操作类似于mixin, 在jQuery中是代码重用的主要技术手段---没有继承也没什么大不了的. <br /><br />11. 名称映射: 一切都是数据 <br />&nbsp; 代码好不好, 循环判断必须少. 循环和判断语句是程序的基本组成部分, 但是优良的代码库中却往往找不到它们的踪影, 因为这些语句的交织会模糊系统的逻辑主线, 使我们的思想迷失在疲于奔命的代码追踪中. jQuery本身通过each, extend等函数已经极大减少了对循环语句的需求, 对于判断语句, 则主要是通过映射表来处理. 例如, jQuery的val()函数需要针对不同标签进行不同的处理, 因此定义一个以tagName为key的函数映射表<br />&nbsp;&nbsp; valHooks: { option: {get:function(){}}}<br />这样在程序中就不需要到处写<br />&nbsp;&nbsp; if(elm.tagName == 'OPTION'){<br />&nbsp;&nbsp;&nbsp;&nbsp; return ...;<br />&nbsp;&nbsp; }else if(elm.tagName == 'TEXTAREA'){<br />&nbsp;&nbsp;&nbsp;&nbsp; return ...;<br />&nbsp;&nbsp; }<br />可以统一处理<br />&nbsp;&nbsp; (valHooks[elm.tagName.toLowerCase()] || defaultHandler).get(elm);<br />&nbsp; &nbsp;<br />&nbsp; 映射表将函数作为普通数据来管理, 在动态语言中有着广泛的应用. 特别是, 对象本身就是函数和变量的容器, 可以被看作是映射表. jQuery中大量使用的一个技巧就是利用名称映射来动态生成代码, 形成一种类似模板的机制. 例如为了实现myWidth和myHeight两个非常类似的函数, 我们不需要<br />&nbsp; jQuery.fn.myWidth = function(){<br />&nbsp;&nbsp; &nbsp;&nbsp; return parseInt(this.style.width,10) + 10;<br />&nbsp;&nbsp; &nbsp;}<br />&nbsp;&nbsp; &nbsp;<br />&nbsp;&nbsp; &nbsp;jQuery.fn.myHeight = function(){<br />&nbsp;&nbsp; &nbsp;&nbsp; return parseInt(this.style.height,10) + 10;<br />&nbsp;&nbsp; &nbsp;}<br />而可以选择动态生成<br />&nbsp;&nbsp; &nbsp;jQuery.each(['Width','Height'],function(name){<br />&nbsp;&nbsp; &nbsp;&nbsp; jQuery.fn['my'+name] = function(){<br />&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; return parseInt(this.style[name.toLowerCase()],10) + 10;<br />&nbsp;&nbsp; &nbsp;&nbsp; }<br />&nbsp;&nbsp; &nbsp;});<br />&nbsp; <br />12. 插件机制：其实我很简单&nbsp;&nbsp; &nbsp;<br />&nbsp; jQuery所谓的插件其实就是$.fn上增加的函数, 那这个fn是什么东西?<br />&nbsp; (function(window,undefined){<br />&nbsp;&nbsp;&nbsp; // 内部又有一个包装<br />&nbsp;&nbsp;&nbsp; var jQuery = (function() {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var jQuery = function( selector, context ) {<br />&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return new jQuery.fn.init( selector, context, rootjQuery );<br />&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ....<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // fn实际就是prototype的简写<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; jQuery.fn = jQuery.prototype = {<br />&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; constructor: jQuery,<br />&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; init: function( selector, context, rootjQuery ) {...&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp; &nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 调用jQuery()就是相当于new init(), 而init的prototype就是jQuery的prototype<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; jQuery.fn.init.prototype = jQuery.fn;<br />&nbsp;&nbsp; &nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 这里返回的jQuery对象只具备最基本的功能, 下面就是一系列的extend<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return jQuery;<br />&nbsp;&nbsp;&nbsp; })(); &nbsp;<br />&nbsp;&nbsp;&nbsp; ...<br />&nbsp;&nbsp;&nbsp;&nbsp; // 将jQuery暴露为全局对象<br />&nbsp;&nbsp;&nbsp; window.jQuery = window.$ = jQuery;<br />&nbsp; })(window);<br />&nbsp; 显然, $.fn其实就是jQuery.prototype的简写. <br />&nbsp; <br />&nbsp; 无状态的插件仅仅就是一个函数, 非常简单. <br />&nbsp; // 定义插件<br />&nbsp; (function($){<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $.fn.hoverClass = function(c) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return this.hover(<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; function() { $(this).toggleClass(c); }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; );<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; };<br />&nbsp; })(jQuery);<br />&nbsp; <br />&nbsp; // 使用插件<br />&nbsp; $('li').hoverClass('hover');<br />&nbsp; <br />&nbsp;对于比较复杂的插件开发, jQuery UI提供了一个widget工厂机制, <br />&nbsp;$.widget("ui.dialog", {<br />&nbsp;&nbsp; options: {<br />&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;autoOpen: true,...<br />&nbsp;&nbsp; &nbsp; },<br />&nbsp;&nbsp; &nbsp; _create: function(){ ... },<br />&nbsp;&nbsp; &nbsp; _init: function() {<br />&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;if ( this.options.autoOpen ) {<br />&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;this.open();<br />&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />&nbsp;&nbsp; &nbsp; },<br />&nbsp;&nbsp; &nbsp; _setOption: function(key, value){ ... }<br />&nbsp;&nbsp; &nbsp; destroy: function(){ ... }<br />&nbsp;});<br />&nbsp;<br />&nbsp;调用 $('#dlg').dialog(options)时, 实际执行的代码基本如下所示:<br />&nbsp; this.each(function() {<br />&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;var instance = $.data( this, "dialog" );<br />&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;if ( instance ) {<br />&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;instance.option( options || {} )._init();<br />&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;} else {<br />&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;$.data( this, "dialog", new $.ui.dialog( options, this ) );<br />&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />&nbsp;&nbsp; &nbsp;}<br />&nbsp;可以看出, 第一次调用$('#dlg').dialog()函数时会创建窗口对象实例,并保存在data中, 此时会调用_create()和_init()函数, 而如果不是第一次调用, 则是在已经存在的对象实例上调用_init()方法. 多次调用$('#dlg').dialog()并不会创建多个实例. <br /><br />13. browser sniffer vs. feature detection<br />&nbsp; 浏览器嗅探(browser sniffer)曾经是很流行的技术, 比如早期的jQuery中<br />&nbsp; jQuery.browser = {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; version:(userAgent.match(/.+(?:rv|it|ra|ie)[/: ]([d.]+)/) || [0,'0'])[1],<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; safari:/webkit/.test(userAgent),<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; opera:/opera/.test(userAgent),<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; msie:/msie/.test(userAgent) &amp;&amp; !/opera/.test(userAgent),<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mozilla:/mozilla/.test(userAgent) &amp;&amp; !/(compatible|webkit)/.test(userAgent)<br />&nbsp; };<br />&nbsp; 在具体代码中可以针对不同的浏览器作出不同的处理<br />&nbsp; if($.browser.msie) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // do something<br />&nbsp; } else if($.browser.opera) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // ...<br />&nbsp; } &nbsp;<br /><br />&nbsp; 但是随着浏览器市场的竞争升级, 竞争对手之间的互相模仿和伪装导致userAgent一片混乱, 加上Chrome的诞生, Safari的崛起, IE也开始加速向标准靠拢, sniffer已经起不到积极的作用. 特性检测(feature detection)作为更细粒度, 更具体的检测手段, 逐渐成为处理浏览器兼容性的主流方式. <br />&nbsp; jQuery.support = {<br />&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;// IE strips leading whitespace when .innerHTML is used<br />&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;leadingWhitespace: ( div.firstChild.nodeType === 3 ),<br />&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;...<br />&nbsp;&nbsp; &nbsp;}<br />&nbsp;&nbsp; &nbsp;只基于实际看见的,而不是曾经知道的, 这样更容易做到兼容未来. <br /><br />14. Prototype vs. jQuery<br />&nbsp; prototype.js是一个立意高远的库, 它的目标是提供一种新的使用体验，参照Ruby从语言级别对javascript进行改造，并最终真的极大改变了js的面貌。$, extends, each, bind...这些耳熟能详的概念都是prototype.js引入到js领域的. 它肆无忌惮的在window全局名字空间中增加各种概念, 大有谁先占坑谁有理, 舍我其谁的气势. 而jQuery则扣扣索索, 抱着比较实用化的理念, 目标仅仅是write less, do more而已. &nbsp;<br />&nbsp; 不过等待激进的理想主义者的命运往往都是壮志未酬身先死. 当prototype.js标志性的bind函数等被吸收到ECMAScript标准中时, 便注定了它的没落. 到处修改原生对象的prototype, 这是prototype.js的独门秘技, 也是它的死穴. 特别是当它试图模仿jQuery, 通过Element.extend(element)返回增强对象的时候, 算是彻底被jQuery给带到沟里去了. prototype.js与jQuery不同, 它总是直接修改原生对象的prototype, 而浏览器却是充满bug, 谎言, 历史包袱并夹杂着商业阴谋的领域, 在原生对象层面解决问题注定是一场悲剧. 性能问题, 名字冲突, 兼容性问题等等都是一个帮助库的能力所无法解决的. Prototype.js的2.0版本据说要做大的变革, 不知是要与历史决裂, 放弃兼容性, 还是继续挣扎, 在夹缝中求生.</div><img src ="http://www.blogjava.net/canonical/aggbug/367146.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/canonical/" target="_blank">canonical</a> 2011-12-25 21:23 <a href="http://www.blogjava.net/canonical/archive/2011/12/25/367146.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++配置管理</title><link>http://www.blogjava.net/canonical/archive/2008/01/12/174892.html</link><dc:creator>canonical</dc:creator><author>canonical</author><pubDate>Sat, 12 Jan 2008 12:58:00 GMT</pubDate><guid>http://www.blogjava.net/canonical/archive/2008/01/12/174892.html</guid><wfw:comment>http://www.blogjava.net/canonical/comments/174892.html</wfw:comment><comments>http://www.blogjava.net/canonical/archive/2008/01/12/174892.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/canonical/comments/commentRss/174892.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/canonical/services/trackbacks/174892.html</trackback:ping><description><![CDATA[&nbsp; 自从离开学校就基本上不再使用C++了，最近却又因为项目上的原因重新走入这一迷失的世界, 感觉很是缺乏一些顺手的工具。首先就是做配置管理有点麻烦, 因为缺乏反射机制, 无法直接映射, 所以一般需要手工书写配置设置功能. <br />
&nbsp; 我们希望配置类在配置阶段能够支持动态属性名，<br />
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000;">&nbsp;&nbsp;GConfig&nbsp;cfg;<br />
&nbsp;&nbsp;cfg.set(</span><span style="color: #000000;">"</span><span style="color: #000000;">bgColor.b</span><span style="color: #000000;">"</span><span style="color: #000000;">,</span><span style="color: #000000;">3.0</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;cfg.set(</span><span style="color: #000000;">"</span><span style="color: #000000;">lightEnabled</span><span style="color: #000000;">"</span><span style="color: #000000;">,</span><span style="color: #0000ff;">false</span><span style="color: #000000;">);<br />
<br />
&nbsp;&nbsp;t_float&nbsp;b&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;cfg.get(</span><span style="color: #000000;">"</span><span style="color: #000000;">bgColor.b</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;bool&nbsp;l&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;cfg.get(</span><span style="color: #000000;">"</span><span style="color: #000000;">lightEnabled</span><span style="color: #000000;">"</span><span style="color: #000000;">);</span></div>
<br />
&nbsp;&nbsp;&nbsp; 但是内部使用时支持直接的属性访问，便于编译器检查， 也提高运算速度。<br />
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;t_float&nbsp;b&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;cfg.bgColor.b;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;bool&nbsp;l&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;cfg.lightEnabled;</span></div>
<br />
<br />
所幸C++的类型系统能够偷偷的去干很多见不得人的勾当，因此便有了下面这个简易机制。<br />
<br />
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000;">#define&nbsp;S_P(x)&nbsp;</span><span style="color: #0000ff;">do</span><span style="color: #000000;">{</span><span style="color: #0000ff;">if</span><span style="color: #000000;">(strcmp(name,#x)&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">0</span><span style="color: #000000;">) { x&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;value;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">; } }&nbsp;</span><span style="color: #0000ff;">while</span><span style="color: #000000;">(</span><span style="color: #000000;">0</span><span style="color: #000000;">)<br />
#define&nbsp;G_P(x)&nbsp;</span><span style="color: #0000ff;">do</span><span style="color: #000000;">{</span><span style="color: #0000ff;">if</span><span style="color: #000000;">(strcmp(name,#x)&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">0</span><span style="color: #000000;">) { value&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;x;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">; } }&nbsp;</span><span style="color: #0000ff;">while</span><span style="color: #000000;">(</span><span style="color: #000000;">0</span><span style="color: #000000;">)<br />
<br />
</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;_GConfig{<br />
</span><span style="color: #0000ff;">public</span><span style="color: #000000;">:<br />
&nbsp;&nbsp;bool&nbsp;lightEnabled;<br />
<br />
&nbsp;&nbsp;t_float&nbsp;minX;<br />
&nbsp;&nbsp;t_float&nbsp;maxX;<br />
&nbsp;&nbsp;t_float&nbsp;minY;<br />
&nbsp;&nbsp;t_float&nbsp;maxY;<br />
<br />
&nbsp;&nbsp;_GConfig(){<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;initialize&nbsp;all&nbsp;primitive&nbsp;members</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;memset(</span><span style="color: #0000ff;">this</span><span style="color: #000000;">,</span><span style="color: #000000;">0</span><span style="color: #000000;">,sizeof(_GConfig));<br />
&nbsp;&nbsp;}<br />
};<br />
<br />
</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;GConfig:&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;_GConfig{<br />
</span><span style="color: #0000ff;">public</span><span style="color: #000000;">:<br />
&nbsp;&nbsp;GColor&nbsp;bgColor;<br />
<br />
&nbsp;&nbsp;GConfig(){<br />
&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;_variant_t&nbsp;get(</span><span style="color: #0000ff;">const</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">char</span><span style="color: #000000;">*</span><span style="color: #000000;">&nbsp;name){<br />
&nbsp;&nbsp;&nbsp;&nbsp;_variant_t&nbsp;value;<br />
&nbsp;&nbsp;&nbsp;&nbsp;get(name,value);<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;value;<br />
&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;get(</span><span style="color: #0000ff;">const</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">char</span><span style="color: #000000;">*</span><span style="color: #000000;">&nbsp;name,_variant_t</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">&nbsp;value){<br />
&nbsp;&nbsp;&nbsp;&nbsp;G_P(lightEnabled);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;G_P(minX);<br />
&nbsp;&nbsp;&nbsp;&nbsp;G_P(maxX);<br />
&nbsp;&nbsp;&nbsp;&nbsp;G_P(minY);<br />
&nbsp;&nbsp;&nbsp;&nbsp;G_P(maxY);<br />
&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;G_P(bgColor.r);<br />
&nbsp;&nbsp;&nbsp;&nbsp;G_P(bgColor.g);<br />
&nbsp;&nbsp;&nbsp;&nbsp;G_P(bgColor.b);<br />
&nbsp;&nbsp;&nbsp;&nbsp;G_P(bgColor.a);<br />
&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;set(</span><span style="color: #0000ff;">const</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">char</span><span style="color: #000000;">*</span><span style="color: #000000;">&nbsp;name,&nbsp;_variant_t&nbsp;value){<br />
&nbsp;&nbsp;&nbsp;&nbsp;S_P(lightEnabled);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;S_P(minX);<br />
&nbsp;&nbsp;&nbsp;&nbsp;S_P(maxX);<br />
&nbsp;&nbsp;&nbsp;&nbsp;S_P(minY);<br />
&nbsp;&nbsp;&nbsp;&nbsp;S_P(maxY);<br />
&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;S_P(bgColor.r);<br />
&nbsp;&nbsp;&nbsp;&nbsp;S_P(bgColor.g);<br />
&nbsp;&nbsp;&nbsp;&nbsp;S_P(bgColor.b);<br />
&nbsp;&nbsp;&nbsp;&nbsp;S_P(bgColor.a);<br />
&nbsp;&nbsp;}<br />
};<br />
</span></div>
<br />
_variant_t是VC++在&lt;comdef.h&gt;中提供的对变体数据类型的封装。使用S_P和G_P这样的宏可以由编译器检查变量名的正确性。<br />
<img src ="http://www.blogjava.net/canonical/aggbug/174892.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/canonical/" target="_blank">canonical</a> 2008-01-12 20:58 <a href="http://www.blogjava.net/canonical/archive/2008/01/12/174892.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[导入]OpenSource: 超越软件工程</title><link>http://www.blogjava.net/canonical/archive/2007/12/08/166189.html</link><dc:creator>canonical</dc:creator><author>canonical</author><pubDate>Fri, 07 Dec 2007 18:58:00 GMT</pubDate><guid>http://www.blogjava.net/canonical/archive/2007/12/08/166189.html</guid><wfw:comment>http://www.blogjava.net/canonical/comments/166189.html</wfw:comment><comments>http://www.blogjava.net/canonical/archive/2007/12/08/166189.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/canonical/comments/commentRss/166189.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/canonical/services/trackbacks/166189.html</trackback:ping><description><![CDATA[<font style="color: #000000;" size="3">&nbsp;&nbsp;&nbsp; 现在讲软件工程的, 所谈论的多半是项目工程,
即如何在有限的时间内配置使用有限的资源在单个项目中达到既定的目标. 传统上, 在这一领域基于预测和计划的瀑布方法曾经占据主流,
但是随着项目的日益复杂化, 各种基于演化(evolution)思想的工程方法在实证中逐渐发展起来. 在时空跨度更大的软件工程领域,
例如延展到软件的不同版本以及多个相似项目的工程中, 演化更是逐渐取得了无可置疑的主导地位. 但是, 从另一个方面说,
目前所有这些软件工程方法所推崇的演化实际上都是非常有限的, 它们通过迭代(iteration)所能够描述的演化过程都只是片断性的,
例如一个项目中的演化, 一个软件产品的演化, 最大的莫过于一整条软件产品线的演化. 所有这些演化过程都面临着一个天然的屏障:
商业公司.在公司内部, 知识或代码可以由开发人员携带到下一个项目, 或者从一个小组传播到另外一个小组, 在新的基础上继续演化的进程.
但是核心的知识或者代码一般只能通过商业交易传达到其他公司, 这是一条非常受限制的途径. 而一个单个公司所开发的软件包, 即使是平台级的产品,
如果只是内部使用, 受限于该公司所从事的业务领域, 其所面临的使用检验也是非常有限的. 而且出于经济上的原因,
单个公司往往无力支撑多个实现同样功能的解决方案, 因而它倾向于消灭软件中的多样性, 这有可能会进一步限制演化的进程. &nbsp;<br />
&nbsp;&nbsp;&nbsp;
开源(OpenSource)软件为软件演化创造了新的可能性.商业友好的开源软件可以被不同的公司自由的运用于不同的业务,
因而可以参与到不同的局部演化过程中. 在应用的过程中, 开源软件面临着巨大的重构压力(这往往是超越了应用最广泛的封闭源码软件包的),
有可能保持更快的演化速度. 而通过对开源软件的回馈, 对开源软件的改进可以传播到时空范围跨度巨大的软件开发过程中. 而且基于源码的开放性,
开发人员的知识交流也随之开放起来. 类比于Darwin进化论, 我们可以说开源驱动了整个软件业界的共同进化(co-evolution).<br />
&nbsp;&nbsp;&nbsp; 多年前, Eric Raymond在著名的文章"大教堂和市集"中 <a href="http://263.aka.org.cn/Docs/c&amp;b.html">http://263.aka.org.cn/Docs/c&amp;b.html</a>,
提出了开源的工程价值, 但其所关注的重点仍然只是单个软件如何在开源的模式下演化, 从今天的观点看来, 这篇战斗檄文已经显得有些局促了.
开源所造就的巨大演化空间远远超越了软件工程所能够提供的. 开源软件现在已经在商业开发领域站稳了脚跟,也渐渐超越了单个公司能够控制的范围.
可以说开源软件的发展是无可逆转的, 我们已经不会也不应该再回复到原先的封闭世界中. </font>
<img src ="http://www.blogjava.net/canonical/aggbug/166189.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/canonical/" target="_blank">canonical</a> 2007-12-08 02:58 <a href="http://www.blogjava.net/canonical/archive/2007/12/08/166189.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于JSF</title><link>http://www.blogjava.net/canonical/archive/2007/07/29/133193.html</link><dc:creator>canonical</dc:creator><author>canonical</author><pubDate>Sun, 29 Jul 2007 15:43:00 GMT</pubDate><guid>http://www.blogjava.net/canonical/archive/2007/07/29/133193.html</guid><wfw:comment>http://www.blogjava.net/canonical/comments/133193.html</wfw:comment><comments>http://www.blogjava.net/canonical/archive/2007/07/29/133193.html#Feedback</comments><slash:comments>7</slash:comments><wfw:commentRss>http://www.blogjava.net/canonical/comments/commentRss/133193.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/canonical/services/trackbacks/133193.html</trackback:ping><description><![CDATA[&nbsp;&nbsp; JSF(Java Server Faces)技术从发布时间上看已经是一种比较古旧的技术了，但是目前仍未能成为主流的开发实践。从我知道这种技术开始, 我对它的判断就与我最早对于EJB的判断一样, 它们都在某种程度上捕获了真正的需求,但是因为它们自身诡异的技术路线.我很怀疑是否这些标准制定者故布疑阵, 便如Microsoft的OLE技术一样, 故意抛出一个错误的方向, 将大批组件开发商带入死局. <br>&nbsp;&nbsp; JSF技术是一种双重的存在：它首先是一种标准，然后也提供了一种缺省的实现。但是从这两方面，我都无法看到JSF的未来。<br>&nbsp;&nbsp; 从设计上说，强类型的视图模型对象层与Witrix的架构设计原则严重冲突。Witrix的基本架构是浏览器和后台服务器通过具有显明语义的url实现两分，这也是所谓REST风格的一种内在要求。隐蔽了链接的技术破坏了基本的web超链模型. 为了获得那么一点点结构控制能力, 做出这样的抽象是不合适的.JSF的配置模型继承了structs的传统，仍然是那样的冗长繁杂。我们是否真的需要这些配置文件，还是希望像ROR那样在代码中直接完成一切？<br>&nbsp;&nbsp; 不能在标准的浏览器中预览. 可以说创造了一个JSF IDE的市场, 但是这无疑是一个无聊的市场. 现在有一些备选的方案, 如Facelets, 使得jsf可以采用属性语法, 但是只要想想仅仅为了这么一点小小的修正所需要付出的开发量就足以让人崩溃。<br>&nbsp;&nbsp; JSF提供了组件级别的事件响应机制，因此似乎是AJAX应用的理想场所．但从Witrix平台的开发实践来看，JSF对于AJAX的使用是受限制的，有着很大局限性的．组件事件响应并不一定要采取JSF那种体系结构．<br>&nbsp;&nbsp; 从实现角度上说，基于jsp tag可以说是JSF的致命弱点之一. jsp tag从设计之始就一直是未经过实践考量，其设计无法支撑复杂的控件架构. 特别是早期JSF与标准的JSP tag不能互通实际上是明显的设计缺陷, 而且性能问题是内置在该设计中的. 现在虽经多个版本的不断补救, 但是为了兼容性, JSP Tag负担过重, 它始终是基于文本处理模型，实际上不可能有什么本质性的进步.　JSP tag模型过分孱弱必然造成JSF设计中大量处理过程堆叠到界面对象层，更加剧了JSF的模型复杂度和性能瓶颈。 实际上根据Witrix平台中tpl模板技术的设计经验，大量界面构建过程是可以在模板层以直观的方式完成的，而不需要借助视图模型对象。<br>&nbsp;&nbsp; 所有问题的一个集中体现就是增加一个新的JSF组件绝对不是一件平凡的事情．如果有一天这个问题可以得到解决，那时的JSF从思想和实现上都必然和现在的JSF有着本质性的区别．<br><img src ="http://www.blogjava.net/canonical/aggbug/133193.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/canonical/" target="_blank">canonical</a> 2007-07-29 23:43 <a href="http://www.blogjava.net/canonical/archive/2007/07/29/133193.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Hibernate动态化</title><link>http://www.blogjava.net/canonical/archive/2006/07/23/59703.html</link><dc:creator>canonical</dc:creator><author>canonical</author><pubDate>Sun, 23 Jul 2006 13:13:00 GMT</pubDate><guid>http://www.blogjava.net/canonical/archive/2006/07/23/59703.html</guid><wfw:comment>http://www.blogjava.net/canonical/comments/59703.html</wfw:comment><comments>http://www.blogjava.net/canonical/archive/2006/07/23/59703.html#Feedback</comments><slash:comments>8</slash:comments><wfw:commentRss>http://www.blogjava.net/canonical/comments/commentRss/59703.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/canonical/services/trackbacks/59703.html</trackback:ping><description><![CDATA[  hibernate的应用中一般总是将entity映射为强类型的java类，这为程序操纵带来很多便利，同时可以将大量动态过程隐蔽在对象包络之下。映射为java类的一个主要问题在于无法在程序运行时刻对于程序进行修改，而数据结构的局部修改几乎是无法避免的。hibernate3本身支持动态数据模型，它允许我们把entity映射为Map数据类型, 当数据结构发生变化的时候, 只需要修改hbm文件即可改变映射模型,而不需要修改java实体类代码. <br />    在hbm定义文件中,如果我们不指定name属性,而是指定entity-name属性,则我们可以将entity映射为Map, 而不是一个java实体类.<br />  &lt;class <br />    entity-name="test.DynamicEntity" <br />    table="DYNAMIC_ENTITY"<br />  &gt;...&lt;/class&gt;<br />  此外, 也可以选择将部分字段动态映射到Map<br />  &lt;class ...&gt;<br />    &lt;dynamic-component name="dynamicAttributes"&gt;<br />      &lt;property name="foo" column="FOO"/&gt;<br />      &lt;property name="bar" column="BAR"/&gt;<br />    &lt;/dynamic-component&gt;<br />  &lt;/class&gt;<br />在HQL语句中可以直接使用o.dynamicAttributes.foo来访问foo属性，所有操作与普通属性相同。<br />  为了实现hiberante映射模型的动态更新，我们首先需要实现sessionFactory的动态更新。目前hibernate的实现只允许从hbm文件重建sessionFactory, 即新建一个sessionFactory替换原有的sessionFactory, 在使用spring的情况下，这需要对org.springframework.orm.hibernate3.LocalSessionFactoryBean进行小小的hack。<br />  为了将动态属性和普通属性同样对待，要求在操作实体对象属性的时候需要能够自动处理nested property, 即需要如下类似的方法：entityDao.getProperty(entity,"dynamicAttributes.foo"), entityDao.setProperty(entity,"dynamicAttributes.foo", attrValue).<br />  为了使得应用程序自动处理新增属性，要求程序是meta驱动的：当实体对象增加了一个属性时，只需要修改meta文件，即可完成对于该属性的增删改查汇总等普通应用需求。<img src ="http://www.blogjava.net/canonical/aggbug/59703.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/canonical/" target="_blank">canonical</a> 2006-07-23 21:13 <a href="http://www.blogjava.net/canonical/archive/2006/07/23/59703.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于Ruby DSL</title><link>http://www.blogjava.net/canonical/archive/2006/07/16/58462.html</link><dc:creator>canonical</dc:creator><author>canonical</author><pubDate>Sun, 16 Jul 2006 14:41:00 GMT</pubDate><guid>http://www.blogjava.net/canonical/archive/2006/07/16/58462.html</guid><wfw:comment>http://www.blogjava.net/canonical/comments/58462.html</wfw:comment><comments>http://www.blogjava.net/canonical/archive/2006/07/16/58462.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/canonical/comments/commentRss/58462.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/canonical/services/trackbacks/58462.html</trackback:ping><description><![CDATA[  最近ruby语言的流行似乎再次引发了DSL(Domain Specific Language)讨论的热潮。从语法表现形式上看，通过对于ruby语言的深度hack, 充分挖掘ruby语言的某些语法特征，可以使得正常的ruby语句看起来比其他计算机语言更接近于人类的自然语言，某些人因此认定ruby语言是DSL的天然载体。但是在我看来，具体语言的语法表达形式对于DSL的核心价值而言并不是最关键的。<br />   首先，DSL的核心在于高效的表达语义，而并不在于是否接近自然语言。接近于自然语言并不意味着更加domain， 因为自然语言也是一种通用语言，它未必能够比采用其他语法形式的语言更加有效的对domain事物进行描述。典型的有数学符号和化学分子式。<br />   第二，作为DSL, 紧凑的表达形式是一方面，另一方面是这种表达形式的稳定性，即如何防止人们写出不符合DSL规范的语句。ruby语言的片断直接作为DSL无疑是一种naive的解决方案，我们可以轻易写出大量不同形式的ruby语句，而它们在语义上是等价的（这意味着通过单元测试也无法发现它们的不同），即人们不按照设计的DSL语法书写，这造成DSL的解体。<br />   作为一种DSL构造语言，其核心能力在于如何将second class的domain中的概念（非语言本身内置的概念）封装到first class的表达形式中。ruby作为一种动态语言，可以更加轻易对于自身meta data进行内省，典型的如ruby中的ActiveRecord设计. 但是在我看来，这种概念提升能力在ruby的语法结构中也是有限的，原因恰在于ruby的语法太多样化了。实际上，我更加看好xml结构的均一性。<img src ="http://www.blogjava.net/canonical/aggbug/58462.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/canonical/" target="_blank">canonical</a> 2006-07-16 22:41 <a href="http://www.blogjava.net/canonical/archive/2006/07/16/58462.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>about GWT</title><link>http://www.blogjava.net/canonical/archive/2006/05/19/47114.html</link><dc:creator>canonical</dc:creator><author>canonical</author><pubDate>Fri, 19 May 2006 13:27:00 GMT</pubDate><guid>http://www.blogjava.net/canonical/archive/2006/05/19/47114.html</guid><wfw:comment>http://www.blogjava.net/canonical/comments/47114.html</wfw:comment><comments>http://www.blogjava.net/canonical/archive/2006/05/19/47114.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.blogjava.net/canonical/comments/commentRss/47114.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/canonical/services/trackbacks/47114.html</trackback:ping><description><![CDATA[  <a href="http://code.google.com/webtoolkit/">http://code.google.com/webtoolkit/</a><br />  最近google发布了Google Web Toolkit(GWT)开发包，这是一种使用java语言开发AJAX应用的开发框架。从技术上看，GWT并没有什么新鲜之处，类似的概念在多年之前就已经有各种尝试了，这些尝试从未真正吸引到足够的注意。GWT的优势也许在于提供了一套模拟工具，另外可能在屏蔽browser的兼容性和bug方面做得更好一些，但是真正的技术思想并没有什么突破. Ruby On Rails同样是试图将ruby语言直接映射到前台程序, 但是它通过一个通用的prototype.js库最小化了ruby语言和js语言之间的区别,在概念上要比GWT的java2js的compiler概念要更加新颖一些. (<a href="http://mir.aculo.us/stuff/COR_20060413_RailsAjax.pdf">http://mir.aculo.us/stuff/COR_20060413_RailsAjax.pdf</a>)<br />  对于web开发而言，我总认为要发挥web的特色，而不是把它约束到其他领域的开发模式上。js+dom+html文本所能做到的结构控制程度要远远超越组件技术，我也从未发现学习java要比学习html要更加容易。也许对于某些对于web一无所知的java开发人员来说，GWT有些意义，也许GWT会特别适合于某些特定的领域，但是作为一种通用的开发框架，我并不看好它。<br /><img src ="http://www.blogjava.net/canonical/aggbug/47114.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/canonical/" target="_blank">canonical</a> 2006-05-19 21:27 <a href="http://www.blogjava.net/canonical/archive/2006/05/19/47114.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>AJAX and AJAH and MVC</title><link>http://www.blogjava.net/canonical/archive/2006/05/09/45295.html</link><dc:creator>canonical</dc:creator><author>canonical</author><pubDate>Tue, 09 May 2006 14:56:00 GMT</pubDate><guid>http://www.blogjava.net/canonical/archive/2006/05/09/45295.html</guid><wfw:comment>http://www.blogjava.net/canonical/comments/45295.html</wfw:comment><comments>http://www.blogjava.net/canonical/archive/2006/05/09/45295.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/canonical/comments/commentRss/45295.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/canonical/services/trackbacks/45295.html</trackback:ping><description><![CDATA[  传统的Mode2模式的服务器端框架在处理AJAX应用的时候存在一定的不适应性，这主要的原因在于Model2基于推模式，它隐含的假设是基于action的处理结果生成整个页面，而AJAX应用中所强调的是页面局部的变化，只更新发生变化的部分，而不是重新生成整个页面（change instead of create), 这两者之间存在着内在的不协调。有些人推崇后台服务程序只返回xml数据的方法，将显示层完全推到前台。虽然在前台通过js脚本操纵DOM节点可以实现非常细粒度上的控制，但是我们并不总是需要最细粒度上的控制权的。例如现在我们在前台实现一个grid控件， grid控件本身只需要控制到单元格层次即可，而不需要对于单元格里存放什么内容有预先的假设. grid.getCell(i,j).innerHTML = cellHtml是非常自然的一种解决方式。完全通过dom来构造界面面临着众多问题，除了浏览器bug这种挥之不去的噩梦之外，在实现过程中我们往往会引入对界面元素的大量限制条件，而无法做到集成各种来源的控件。<br />  在服务器端生成页面片断的方式也称为AJAH，表面上看起来它比AJAX要简易一些，是很多服务器端框架引入AJAX概念的乡间小径。但有趣的是在基于拉模式(pull mode)的服务器端MVC框架中，AJAH是在架构上比AJAX更加灵活的一种方式。在witrix平台的jsplet框架中，web访问的基本形式如下:<br />   /view.jsp?objectName=XXObject&amp;objectEvent=XXEvent&amp;otherArgs&amp;tplPart=XXPart<br />其中objectName对应于后台的服务对象，objectEvent参数映射到服务对象的方法，view.jsp是对于后台对象进行渲染的模板页面，而tplPart参数可以指定只使用模板的某一部分进行渲染。如果我们选择json.jsp或者burlap.jsp作为渲染模板，则可以退化到返回数据而不是内容的方式。在js中进行简单的封装后我们可以通过如下方式进行远程调用：<br />  new js.Ajax().setObjectName("XXObject").setObjectEvent("XXEvent").addForm("XXFormId").callRemote(callbackFunc);<br />   它对应的url请求为<br />   /json.jsp?objectName=XXObject&amp;objectEvent=XXEvent&amp;...<br />对于同样的后台业务处理，我们可以自由的选择渲染模板，则可以很自然的得到更多的处理方式，例如返回javascript代码来实现对于前台的回调。<br /><img src ="http://www.blogjava.net/canonical/aggbug/45295.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/canonical/" target="_blank">canonical</a> 2006-05-09 22:56 <a href="http://www.blogjava.net/canonical/archive/2006/05/09/45295.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[导入]锐道dorado</title><link>http://www.blogjava.net/canonical/archive/2006/04/02/38732.html</link><dc:creator>canonical</dc:creator><author>canonical</author><pubDate>Sun, 02 Apr 2006 06:57:00 GMT</pubDate><guid>http://www.blogjava.net/canonical/archive/2006/04/02/38732.html</guid><wfw:comment>http://www.blogjava.net/canonical/comments/38732.html</wfw:comment><comments>http://www.blogjava.net/canonical/archive/2006/04/02/38732.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/canonical/comments/commentRss/38732.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/canonical/services/trackbacks/38732.html</trackback:ping><description><![CDATA[
		<font style="color: rgb(0, 0, 0);" size="3">
				<a href="http://www.bstek.com/">http://www.bstek.com/</a>
				<br />   
上海锐道的Dorado框架号称是一个基于构件技术的、面向B/S和多层架构体系的web应用开发平台，
其前身称为Extra。从具体功能来看，如果将其看作是一个全功能的web应用开发平台，　那它现在的功能集显得就太单薄了一些,
其主要部分还是提供了一些前台的界面控件, 其他如web框架部分,很像是struts的一个简化版，没有什么特异之处。 <br />    Dorado的技术特点是大量采用ajax技术来实现前台控件. 其前后台交互采用了自定义的基于xml格式的rpc方式, 而数据绑定使用了xml数据岛,例如<br />    &lt;xml id="__datasetEmployee" &gt;<br />    &lt;records&gt;<br />    &lt;record isCurrent="true"  state="none" &gt;<br />    &lt;new&gt;,~73~73~73~73~73,~68~68,~44~31~32,true,true,295113600000,2034.0,,&lt;/new&gt;<br />    &lt;/record&gt;<br />    &lt;/xml&gt;<br />    record内部使用的是Dorado私有的编码/解码规则, 大概是为了压缩数据量吧.<br />   
从Dorado目前提供的界面来看还是比较丰富的,基本满足了一般信息系统开发的需求, 但是其可扩展性似乎并不是很好.
它虽然号称是组件式开发,但是其前台和后台引擎似乎都没有提供完善的组件模型支持, 只是实现了一些既定的界面组件而已. <br />    1. 其前台的js函数中存在着大量针对数据类型的switch语句,似乎其所能够支持的控件类型已经内置在前台引擎中, 如果我们要增加一种新的界面组件大概需要在各处修改引擎代码, 缺乏一种抽象机制. <br />   
2. 后台ViewModel模型似乎是想构造出一个Component架构来, 但这个模型目前看起来明显没有Echo2这样的组件模型精致,
似乎缺乏一种一致的组件重组机制.  Dorado的ViewModel是有状态的, 通过RPC机制,
它实际上可以独立于系统web层与前台控件交互. <br />    3.
Dataset是Dorado中最重要的数据供体接口, 从它所提供的方法 getField,deleteRecord,
insertRecord, movePrev, moveNext, getPageSize等可以看出,
这明显是绑定到了可分页表格的数据模型上. 实际上整个系统设计似乎都隐含假定了一个Table模型, 例如Validator接口中具有一个函数
ViewField getField(), 这里明确假定了validate只能针对单个字段进行, 而不是属于某个整体组件的.<br />    4. Dorado中所有组件的界面代码生成都是以程序方式进行的, 没有模板机制. 因而增加新的控件的实现时, 需要在后台通过java代码输出一部分界面, 在前台通过js脚本动态更新界面, 工作量相当大.<br />    5. Dorado中界面输出应该是通过Outputter接口来进行 <br />      public interface Outputter{<br />        public String getStartOutput(HttpServletRequest req, Object o)throws Exception;<br />        public String getEndOutput(HttpServletRequest req, Object o) throws Exception;<br />      }<br />     
这里一方面与web层形成了绑定，另一方面它必须在内部完整的生成界面代码之后一次性传入response, 这无疑加重了后台的内存压力.
输出分成了StartOutput和EndOutput大概是为了支持布局组件等容器类组件, 相当于是组件内部可以有一个洞, 这与Jsp
Tag模型是匹配的, 但是这种方式很难以高效率实现界面上的结构融合. <br />    7. Dorado似乎没有直接提供组件的再封装机制, 在现有组件中作局部修正往往需要通过代码方式来进行.例如表格中的性别字段需要显示图片而不是文字, 则需要在Column的onRefresh事件中写入下代码,<br />            if (record.getValue("sex")){<br />        cell.innerHTML = "&lt;img src='images/man.gif'&gt;";<br />        }<br />        else{<br />        cell.innerHTML = "&lt;img src='images/woman.gif'&gt;";<br />        }<br />    这明显已经不是可配置的内容了. 如果我所需要增加的代码是一个复杂的组件, 这里就很难进行处理了. <br />    6. Dorado的技术绑定在了IE浏览器上, 无法兼容其它浏览器, 这有些偏离目前的标准化方向. <br /><br />   
目前的快速开发平台的一个共同特点是集中在单表的CRUD(Create Read Update Delete)上,
可以快速完成单表或主从表的增删改查. 这本也是正确方向,毕竟目前系统开发中的大量工作都是重复性的简单劳动,
但是一般系统设计中为了支持单表操作而在建模的时候引入了对表格模型过强的依赖,  大大降低了系统的可扩展性.
另外现在一般web组件框架的设计往往是模仿桌面开发领域的组件模式， 希望提供完全黑箱式的组件调用方式, 这其实是放弃了web开发的优势. 
实际上借助于xml格式的规范性和简单性, 我们完全可以提供更加强大的结构组件, 并把组件封装的能力开放给普通程序员. </font>
<img src ="http://www.blogjava.net/canonical/aggbug/38732.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/canonical/" target="_blank">canonical</a> 2006-04-02 14:57 <a href="http://www.blogjava.net/canonical/archive/2006/04/02/38732.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[导入]同方ezOne</title><link>http://www.blogjava.net/canonical/archive/2006/04/02/38731.html</link><dc:creator>canonical</dc:creator><author>canonical</author><pubDate>Sun, 02 Apr 2006 06:53:00 GMT</pubDate><guid>http://www.blogjava.net/canonical/archive/2006/04/02/38731.html</guid><wfw:comment>http://www.blogjava.net/canonical/comments/38731.html</wfw:comment><comments>http://www.blogjava.net/canonical/archive/2006/04/02/38731.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.blogjava.net/canonical/comments/commentRss/38731.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/canonical/services/trackbacks/38731.html</trackback:ping><description><![CDATA[
		<p>    <a href="http://www.ezone.com.cn/">http://www.ezone.com.cn</a><br />   
同方这个公司在我的印象中更像是个系统集成商，在软件方面没有什么独特之处。不知道是否是待遇问题，我也未听说同方旗下招揽过什么软件高手。同方最近两年
在软件平台方面花了一些气力，据说有70多个人，干了一两年，将同方以前的项目进行提炼，开发了ezOne平台。现在同方各个事业部开发新项目的时候好像
要求使用该平台，不过可能是涉及到总部和各个部门分钱的问题，下面的人未必有很大的应用积极性。<br />
   
同方的特点是做过大量项目，行业经验多，所以ezOne中提供了很多行业组件。ezOne的一个核心部分是portal服务器，是基于Apache项目组
的Jetspeed修改而来，应该是符合JSR168标准的，所强调的概念也正是应用集成。以前扫过一眼ezOne的代码，感觉质量一般，性能可能会有一
些问题，但编码还算工整。同方握有大量国家资源，应该不至于成为平台产品开发商，开发平台的目的大概还是降低自身开发的难度。其产品倾向于符合标准，中规
中矩，大概很难有什么创新之处。ezOne中很多基础组件都是独立开发的，例如存储层，没有使用hibernate等开源软件。<br /></p>
<img src ="http://www.blogjava.net/canonical/aggbug/38731.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/canonical/" target="_blank">canonical</a> 2006-04-02 14:53 <a href="http://www.blogjava.net/canonical/archive/2006/04/02/38731.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[导入]科诺KA-2</title><link>http://www.blogjava.net/canonical/archive/2006/04/02/38730.html</link><dc:creator>canonical</dc:creator><author>canonical</author><pubDate>Sun, 02 Apr 2006 06:50:00 GMT</pubDate><guid>http://www.blogjava.net/canonical/archive/2006/04/02/38730.html</guid><wfw:comment>http://www.blogjava.net/canonical/comments/38730.html</wfw:comment><comments>http://www.blogjava.net/canonical/archive/2006/04/02/38730.html#Feedback</comments><slash:comments>7</slash:comments><wfw:commentRss>http://www.blogjava.net/canonical/comments/commentRss/38730.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/canonical/services/trackbacks/38730.html</trackback:ping><description><![CDATA[   <a href="http://www.kenoah.com/">http://www.kenoah.com</a><br />
  
前两天和科诺的研发经理聊了一会，也简单看了一下他们的演示。因为比较匆忙，没有谈到什么实质性的内容，只是有一个粗浅的印象。科诺目前的发展重点还是国
外的外包业务，其网站上相关介绍材料很少，不过据说今年将投入较大的资源在国内公司建设和市场开拓上。<br />
   
科诺产品的特点是代码生成。经过可视化配置之后，可以根据模板生成jsp源代码，程序员可以基于这些代码进行修改。据说遵守一定的规则之后，自动生成的代
码与手写代码是相互隔离的，自动生成的代码可以无限多次生成而不影响手写代码质量，但我未看到演示。科诺生成的页面只支持IE,
不支持Firefox等浏览器。大概是因为其从事的主要是国外外包业务，其界面的美观程度一般。虽然可以修改页面模板来改变界面风格，但从我实际看到的模
板代码而言，重新写一套并不太容易。<br />
   
科诺产品的功能大概是可以完成单表或者主从表的增删改查，并配合一个或者多个业务流程。其工作流引擎据说是符合WFMC规范的，但从实际操作上看，似乎不
是一个通用的工作流引擎。至少给我演示的时候，工作流步骤的每一步，actor所能采取的操作是固定的，如退回，通过等，似乎是为审批定制的。<br />
   
与普元EOS相比，科诺在功能上还是要弱不少。在开发工具上，也显得要简单许多。与普元EOS类似的是，科诺的产品似乎也只是利用工具根据现成的模板制造
固定模式的代码，在设计思想方面并没有什么本质性的突破，与其宣传的组件思想相差甚远。如果要超越自动生成的代码作一些事情，这些平台都无法提供什么有力
的支持。科诺所谓的业务组件似乎就是对table的描述，其设计工具不算太好，至少我没有找到一个汇总视图让我可以看到所有已经配置的字段属性。在设置复
杂字段或者汇总字段方面，科诺的产品也有很多限制，不太灵活。在前台框架方面，科诺写了一个类似于struts的框架，其他就乏善可承了。流程方面的设置
被称为所谓的流程组件，运行时可以通过一个js库在界面上进行展现。不同的业务组件应该可以对应到同一流程组件，这大概就是科诺所谓的组件重用了吧。开发
工程文件以xml格式进行存储，看了一眼，相当复杂，竟然还和SOAP有关系，不知道他们为什么这么设计（符合标准?）。<br />
    在科诺的另外一个印象是，公司里有不少女性程序员在干活。看来至少他们的产品可以由初级程序员掌握并满足美国外包开发的需求。<img src ="http://www.blogjava.net/canonical/aggbug/38730.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/canonical/" target="_blank">canonical</a> 2006-04-02 14:50 <a href="http://www.blogjava.net/canonical/archive/2006/04/02/38730.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[导入]再谈普元EOS中的数据总线</title><link>http://www.blogjava.net/canonical/archive/2006/04/02/38729.html</link><dc:creator>canonical</dc:creator><author>canonical</author><pubDate>Sun, 02 Apr 2006 06:47:00 GMT</pubDate><guid>http://www.blogjava.net/canonical/archive/2006/04/02/38729.html</guid><wfw:comment>http://www.blogjava.net/canonical/comments/38729.html</wfw:comment><comments>http://www.blogjava.net/canonical/archive/2006/04/02/38729.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/canonical/comments/commentRss/38729.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/canonical/services/trackbacks/38729.html</trackback:ping><description><![CDATA[   前两天一位普元的朋友衣风<a href="http://blog.sina.com.cn/u/1495516693">http://blog.sina.com.cn/u/1495516693</a>在我的blog上留言,谈到对数据总线的不同看法. 我本人并未使用过普元EOS,所做的判断只是基于我个人肤浅的第一印象, 很有可能是不准确的. 不过, 目前我仍然坚持自己对于普元EOS的看法,呵呵.<br />
    <br />
    1. EOS产生的xml描述文件中的大量条目都是EOS自身的结构要求，而与实际业务无关，即EOS描述文件中的有效信息量密度很低.<br />
    衣风认为条目并不是EOS自身的结构要求，而是业务对象的结构描述.
这里我的看法是业务对象应该尽量通过领域(Domain)语言来描述, 领域信息的外在表象应该尽量是卷缩的,而不是展开的,
应该尽量是抽象的而不是实现细节层面上的.例如:<br />
    &lt;function class="test.MyFunctionProvider"&gt;<br />
        &lt;args&gt;<br />
            &lt;arg&gt;<br />
                &lt;name&gt;argA&lt;/name&gt;<br />
                &lt;value&gt;3&lt;/value&gt;<br />
            &lt;/arg&gt;<br />
        &lt;/args&gt;<br />
    &lt;/function&gt;<br />
    以上信息可以描述一个方法调用, 这里的function, args, arg, name,
value等标签的设置都是解析器为了理解该调用的语义而规定的结构元素,这些元素并不属于函数本身.
例如我们可以规定如下的调用格式来简化描述文件而不损失信息,<br />
    &lt;function class="test.MyFunctionProvider"&gt;<br />
        &lt;arg name="argA"&gt;3&lt;/arg&gt;<br />
    &lt;/function&gt;<br />
    而在我们的工作流引擎中, 业务操作的调用以封装后的形式出现<br />
    &lt;BusinessA:DoWorkA argA="3"/&gt;<br />
    通过标签封装之后, 我们在调用的时候所需要提供的信息是最小化的, 每一个涉及到的元素都是有着业务含义的,
而将解析器本身的结构要求隐蔽在封装的标签定义中.此后我们如果更换了实现,而业务需求不变, 则我们的调用处是不会受到影响的. <br />
    现在基于xml语法的文件格式很多, 我们的工作流引擎也采用了xml描述. 但是我们的一个基本观点是,
xml配置文件解析器不应该直接理解文件中所有的标签, 即它只需要理解自身的结构元素, 而为引入Domain知识保留余地.<br />
    <br />
    2. 普元EOS中的结构似乎很难进行有效的扩展。而所谓的xml总线技术更加剧了这一点 <br />
   
衣风认为"正是将XML作为数据传递的总线，才使应用在数据层次上具有了较强的扩展能力"。从面向对象的观点看,
程序中普适性的基本基元是数据与行为的集合体, 而程序模块之间的交互也绝不仅仅是通过数据来进行, 而是往往呈现出一种数据与行为的交织状况.
普元的模型应该包含了大量发生紧密关联的局部元素, 它们应该处在同一进程(状态)空间中, 直接访问对象应该是最简单,最经济,
最完备的信息传递方式,
而"xml节点的表达能力远远超越了普通的数据类型，但充其量也不过是对现有数据的规整的树形表示，并不具有动态计算能力（甚至是最简单的lazy
evaluation）". 实际上对于所谓的总线, 最简单的模型是一个可以动态管理的变量集合, 那么一个Map就足够了.
在集合中我们可以保存各种对象, 比如xml节点, 但是又不限于xml节点. 从建模的角度上说,
把xml节点定义为一级集合元素我认为是不合适的. 通过对象的成员函数, 我们在对象图中可以包含大量的动态计算信息, 例如<br />
     obj.getSomeCalculatedAttribute().getLazyLoadValue()<br />
    这些动态语义在纯数据含义的xml总线技术中不知道如何表达. <br />
    对象图表达数据关联的能力也强于树形结构的xml节点, 例如 obj.getRefObj().getRefObj() == obj, 不知道这样的关联在普元EOS的数据总线中如何表达. <br />
    在并发访问中如果需要同步, 对于对象, 我们可以使用synchronized机制, 但是对于xml节点不知道如何才能有通用的处理方式.  <img src ="http://www.blogjava.net/canonical/aggbug/38729.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/canonical/" target="_blank">canonical</a> 2006-04-02 14:47 <a href="http://www.blogjava.net/canonical/archive/2006/04/02/38729.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[导入]普元EOS</title><link>http://www.blogjava.net/canonical/archive/2006/04/02/38728.html</link><dc:creator>canonical</dc:creator><author>canonical</author><pubDate>Sun, 02 Apr 2006 06:45:00 GMT</pubDate><guid>http://www.blogjava.net/canonical/archive/2006/04/02/38728.html</guid><wfw:comment>http://www.blogjava.net/canonical/comments/38728.html</wfw:comment><comments>http://www.blogjava.net/canonical/archive/2006/04/02/38728.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/canonical/comments/commentRss/38728.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/canonical/services/trackbacks/38728.html</trackback:ping><description><![CDATA[
		<p>     <a href="http://www.primeton.com/">http://www.primeton.com/</a></p>
    普
元软件公司是国内专业的中间件提供商，从国家得到了不少投资，做出来的东西也是相当的庞大。最近普元EOS的宣传和发展的势头都很盛。其宣传材料中屡次提
到“软件的涅磐“这一用语，这明显是一种危言耸听之举，当然这在业内也不算什么新鲜的事情。按照EOS的宣传，"以图形化的构件组装方式“画”出来的软件
无论从结构上、形式上还是开发过程上都堪称简捷而美的软件"。这一提法倒是别开生面。图形化与简洁，与美竟然还存在着这样必然的联系，实在是一种创举。<br />
    
从普元公开的资料来看，EOS的一个鲜明特征是全面基于xml描述，即所谓的xml数据总线。表面上看起来，xml结构内置于系统内核中似乎很时尚，但实
际上，EOS产生的xml描述文件中的大量条目都是EOS自身的结构要求，而与实际业务无关，即EOS描述文件中的有效信息量密度很低。这是一个危险的信
号。EOS的xml描述本身可以看作是一种完全新的编程语言，但这个语言似乎没有什么抽象能力和组合能力，对于关联的表达能力也很弱（到处都是数字
id)。如果直接手工编写，那是一件要死人的事情。只有通过集成开发环境的可视化界面，EOS才呈现出可理解的一面。<br />
    
EOS的概念与Language
Workbench是不同的，其中的结构似乎很难进行有效的扩展。而所谓的xml总线技术更加剧了这一点。xml数据总线其实与面向过程编程类似，只是过
程变成了service，数据变成了xml节点而已。对象与简单数据结构在结构表达上的本质差异就在于对象通过成员函数可以封装动态结构。虽然xml节点
的表达能力远远超越了普通的数据类型，但充其量也不过是对现有数据的规整的树形表示，并不具有动态计算能力（甚至是最简单的lazy
evaluation）。丧失了动态计算能力，就意味着我们很难在系统中动态引入结构，程序中所操纵的结构都需要事前定义出来，这将极大的限制系统的可扩
展性。另一方面，xml节点受限于自身格式，其描述关联的能力也要弱于java对象结构本身。对象通过引用访问相关对象，其隐含意义是对象处于同一地址
（状态）空间中，可以非常自然的保证对象的唯一性并实现同步访问。在跨越状态空间的边界时，xml表示是有意义的，因为我们需要把所有的结构都暴露出来并
加以描述(外在化）。而在状态空间内部，我们需要更加紧致有效的表述方式。<br />
     在具体的实现中，
EOS暴露给程序员的xml操纵API相当的原始，使用起来很繁琐。在前台展示页面中，如果不使用EOS的界面组件，提取数据本身就是一种不小的困难。
EOS的前台展示组件与后台的结合也比较弱，后台改变之后，缺乏有效的手段来检测并保证前后台结构的同步性。所谓的前台构件层似乎只是提供了一些帮助函数
和功能固化的组件，并没有提供什么有效的利于结构抽象和结构重组的机制。<br />
     整个EOS的构架看起来很象是一个monster, 我很难想象它的各个部分如何才能独立的，深入的发展下去。<img src ="http://www.blogjava.net/canonical/aggbug/38728.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/canonical/" target="_blank">canonical</a> 2006-04-02 14:45 <a href="http://www.blogjava.net/canonical/archive/2006/04/02/38728.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[导入]Ajax结构分析</title><link>http://www.blogjava.net/canonical/archive/2006/02/22/32012.html</link><dc:creator>canonical</dc:creator><author>canonical</author><pubDate>Wed, 22 Feb 2006 12:33:00 GMT</pubDate><guid>http://www.blogjava.net/canonical/archive/2006/02/22/32012.html</guid><wfw:comment>http://www.blogjava.net/canonical/comments/32012.html</wfw:comment><comments>http://www.blogjava.net/canonical/archive/2006/02/22/32012.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/canonical/comments/commentRss/32012.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/canonical/services/trackbacks/32012.html</trackback:ping><description><![CDATA[<p>Ajax: A New Approach to Web Applications <a href="http://www.adaptivepath.com/publications/essays/archives/000385.php">http://www.adaptivepath.com/publications/essays/archives/000385.php</a><br>Ajax(Asynchronous
JavaScript +
XML)并不是一个革命性的崭新概念（也许根本就不存在突发的革命），它的技术基础在多年之前就已经牢固的建立起来了，在概念层次上的探讨也早就不是一个
新鲜的话题，只是大规模的有深度的应用似乎是最近才开始的。<br>
从广义上说，web应用至少涉及到两个结构，<br>
1. 后台以java语言表达的业务逻辑结构 <br>
2。前台以html语言表达的界面表现结构。</p>
<p>web开发很大一部分工作就是建立这两个结构之间的关系。即我们需要<br>
html &lt;--&gt; java</p>
<p>我
们首先要意识到这两种结构之间并不一定是同构的，即后台数据的组织方式与前台展现时的结构是不同的。同样的数据可以对应于不同的展现结构。这也是所谓
MVC架构实现模型与视图分离的依据。传统上，基于Model2模式的MVC框架中，这两种结构的映射只能在很粗的粒度上进行（即整个页面的粒度上），因
此妨碍了封装和重用。为了进行细粒度的映射，我们必须要拥有细粒度的结构控制能力。而目前最强的结构控制能力存在于javascript/DHTML模型
之中，在js中html的结构可以是一段线性的文本(innerHTML和outerHTML), 可以是层级组织的节点（parentNode,
childNodes),
也可以是按照key组织起来的Map(getElementById)。在不同的情形下，我们可以根据需要选择不同的结构模型。<br>
ajax体系很直接的一个想法就是将所有关于界面表达和控制的结构都推到前台，由控制力最强的js来控制后台数据模型与前台html模型之间的映射。<br>
html &lt;--&gt; js &lt;==&gt; xml &lt;==&gt; java <br>
在ajax
体系中，xml所扮演的角色是js与java之间的翻译通道，它将js中的结构与java中的结构对应起来，这种翻译比html/java之间的映射要简
单的多。其实它甚至可以是一种同构的映射，可以用一种通用的方式来进行，例如结合burlap与buffalo包的功能。结合webservice的一些
思想，js/java之间的映射是可以在函数调用这种细粒度上自动进行的，从而我们最终可以在概念上完成html/java之间的细粒度映射。</p>
				  
             <img src ="http://www.blogjava.net/canonical/aggbug/32012.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/canonical/" target="_blank">canonical</a> 2006-02-22 20:33 <a href="http://www.blogjava.net/canonical/archive/2006/02/22/32012.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[导入]代码复用(reuse</title><link>http://www.blogjava.net/canonical/archive/2006/01/23/29054.html</link><dc:creator>canonical</dc:creator><author>canonical</author><pubDate>Mon, 23 Jan 2006 15:01:00 GMT</pubDate><guid>http://www.blogjava.net/canonical/archive/2006/01/23/29054.html</guid><wfw:comment>http://www.blogjava.net/canonical/comments/29054.html</wfw:comment><comments>http://www.blogjava.net/canonical/archive/2006/01/23/29054.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/canonical/comments/commentRss/29054.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/canonical/services/trackbacks/29054.html</trackback:ping><description><![CDATA[<p>&nbsp;&nbsp;&nbsp;
代码复用包括两个方面：概念复用和实现复用。这两者在C++的虚拟函数设计中是合二为一的，结果概念上的模糊往往造成继承机制的滥用。为了复用我们往往在
基类中塞入过多的职责，并在程序中制造了过多的层次。java的interface是纯粹的概念复用机制，实现方面的复用我们一般通过Impls类或者
Utils类来进行，即将代码片断写为静态函数。一般应该避免在类中写特别多的帮助性成员函数，因为成员函数隐含的通过成员变量相关着，比静态函数要更加
难以控制。<br>
&nbsp;&nbsp;&nbsp;
类是一个整体的概念，整体概念失效了，类也就不存在了。从这一点上来说，它未必是比静态函数更加稳定。概念与实现是两个不同层面的东西。实际上它们一般也
是多对多的关系。同一个概念可能换用多种不同的实现，而同一段功能代码也可能在多个类中使用。<br>
&nbsp;&nbsp;&nbsp;
代码复用的意义不仅仅在于减少工作量。实际上复用是对软件的一种真正的检验，而测试仅仅是一种模拟的检验而已。每一次复用都是对代码的一次拷问。在不断使
用中感受到不同使用环境中的各种压力，才能实现概念的不断精化并确保实现的正确性。<br></p>
				  
             <img src ="http://www.blogjava.net/canonical/aggbug/29054.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/canonical/" target="_blank">canonical</a> 2006-01-23 23:01 <a href="http://www.blogjava.net/canonical/archive/2006/01/23/29054.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于DAO和Service</title><link>http://www.blogjava.net/canonical/archive/2006/01/23/29053.html</link><dc:creator>canonical</dc:creator><author>canonical</author><pubDate>Mon, 23 Jan 2006 14:57:00 GMT</pubDate><guid>http://www.blogjava.net/canonical/archive/2006/01/23/29053.html</guid><wfw:comment>http://www.blogjava.net/canonical/comments/29053.html</wfw:comment><comments>http://www.blogjava.net/canonical/archive/2006/01/23/29053.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/canonical/comments/commentRss/29053.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/canonical/services/trackbacks/29053.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp; 我们开发程序的目的是为了完成业务功能, 理想的情况下程序中的每一条语句都应该是与业务直接相关的,
例如程序中不应该出现连接数据库, 读取某个字段等纯技术性的操作, 而应该是得到用户A的基本信息等具有业务含义的操作. dao(data
access object)层存在的意义在于将与数据持久化相关的函数调用剥离出去, 提供一个具有业务含义的封装层. 原则上说,
dao层与utils等帮助类的功能非常类似, 只是更加复杂一些, 需要依赖更多的对象(如DataSource,
SessionFactory)等. 如果不需要在程序中屏蔽我们对于特定数据持久层技术的依赖, 例如屏蔽对于Hibernate的依赖,
在dao层我们没有必要采用接口设计. 一些简单的情况下我们甚至可以取消整个dao层, 而直接调用封装好的一些通用dao操作函数,
或者调用通用的EntityDao类等. <br>
&nbsp;&nbsp;&nbsp; 程序开发的过程应该是从业务对象层开始的, 并逐步将纯技术性的函数调用剥离到外部的帮助类中,
同时我们会逐渐发现一些业务操作的特定组合也具有明确的含义, 为了调用的方便, 我们会把它们逐步补充到service层中. 在一般的应用中,
业务逻辑很难稳定到可以抽象出接口的地步, 即一个service接口不会对应于两个不同的实现, 在这种情况下使用接口往往也是没有必要的. <br>
&nbsp;&nbsp; &nbsp;<br>
&nbsp;&nbsp;&nbsp; 在使用spring的情况下原则上应该避免使用getBean的调用方式, 应该尽量通过注入来获得依赖对象, 但有时我们难免需要直接获取业务对象, 在不使用接口的情况下可以采用如下方式<br>
<br>
&nbsp;&nbsp;&nbsp; class TaskService{<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public static TaskService getInstance(){<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return (TaskService)BeanLoader.getBean(TaskService.class);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>
&nbsp;&nbsp;&nbsp; }<br>
<br>
&nbsp;&nbsp;&nbsp;
在程序中我们可以直接使用TaskService.getInstance()来得到TaskService对象.通过命名规范的约定,
我们可以从类名推导出spring配置文件中的对象名, 因而不需要使用一个额外的硬编码字符串名. <br>
<br>
<img src ="http://www.blogjava.net/canonical/aggbug/29053.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/canonical/" target="_blank">canonical</a> 2006-01-23 22:57 <a href="http://www.blogjava.net/canonical/archive/2006/01/23/29053.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>web程序中的scope</title><link>http://www.blogjava.net/canonical/archive/2006/01/15/28130.html</link><dc:creator>canonical</dc:creator><author>canonical</author><pubDate>Sun, 15 Jan 2006 14:35:00 GMT</pubDate><guid>http://www.blogjava.net/canonical/archive/2006/01/15/28130.html</guid><wfw:comment>http://www.blogjava.net/canonical/comments/28130.html</wfw:comment><comments>http://www.blogjava.net/canonical/archive/2006/01/15/28130.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/canonical/comments/commentRss/28130.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/canonical/services/trackbacks/28130.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;
jsp模型为web程序提供了page/request/session/application这四个基础性的变量域.
这种变量域的划分很大程度上是纯技术性的, 与我们的业务应用中需要的scope支持相去甚远. 当我们把业务对象的生命周期映射到这些变量域的时候,
经常出现不适应的情况. 例如我们可能被迫选择把与某项业务相关的所有数据放置在session中并在各处硬编码一些资源清理代码.
为了实现与愈来愈复杂的应用开发的契合, 我们需要能够在程序中定义与应用相关的变量域并实现对这些变量域的管理,
即我们需要一种自定义scope的支持而不是使用几个固定的scope. <br>

&nbsp;&nbsp;&nbsp; JBoss的Seam项目<a href="http://www.jboss.com/products/seam">http://www.jboss.com/products/seam</a> 中引入了一种所谓declarative application state management的机制<br>

&nbsp;&nbsp;&nbsp; <a href="http://blog.hibernate.org/cgi-bin/blosxom.cgi/Gavin%20King/components.html">http://blog.hibernate.org/cgi-bin/blosxom.cgi/Gavin%20King/components.html</a>,
其中的关键是增加了business process和conversation这两个应用直接相关的scope, 它们都是可以根据需要自由创建的.
business process context使用jBPM支持long running的状态保持. 而conversation
context是对session使用的一种精细化, 与beehive项目中的page flow所需的scope支持非常类似 <a href="http://beehive.apache.org/docs/1.0m1/pageflow/pageflow_overview.html">http://beehive.apache.org/docs/1.0m1/pageflow/pageflow_overview.html</a>. 但目前seam中的scope支持仍是非常原始的, 不支持嵌套的context, 这意味着对于复杂应用尚无控制和管理能力.<img src ="http://www.blogjava.net/canonical/aggbug/28130.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/canonical/" target="_blank">canonical</a> 2006-01-15 22:35 <a href="http://www.blogjava.net/canonical/archive/2006/01/15/28130.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>xpath selector vs. css selector</title><link>http://www.blogjava.net/canonical/archive/2006/01/08/27196.html</link><dc:creator>canonical</dc:creator><author>canonical</author><pubDate>Sun, 08 Jan 2006 15:21:00 GMT</pubDate><guid>http://www.blogjava.net/canonical/archive/2006/01/08/27196.html</guid><wfw:comment>http://www.blogjava.net/canonical/comments/27196.html</wfw:comment><comments>http://www.blogjava.net/canonical/archive/2006/01/08/27196.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/canonical/comments/commentRss/27196.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/canonical/services/trackbacks/27196.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp; 在无侵入性的前台页面控件设计方案中, 我们需要一种简便的方法迅速定位页面中的某一节点(dom
node). 使用xpath是非常诱人的一个技术选择, 但是在实际使用中, 我们却发现xpath并不是那么方便. xpath的能力非常强大,
它支持绝对定位, 例如//input[@id='3'], 也支持相对定位, 例如 ./input[0], 甚至支持根据节点内容定位,
例如//a[contains(., 'partial text')]. <br>
&nbsp;&nbsp;&nbsp; 问题是在一个复杂的界面控件中, html节点本身的结构与界面展现结构并不是一致的,例如一个特定效果的边框可能需要多个html元素互相嵌套才能够实现, 因此xpath的相对路径选择能力往往派不上用场(除非是提供<a href="http://www.backbase.com/">http://www.backbase.com/</a>那
样的界面抽象层), 而根据内容定位的方式过于灵活, 难以维护一个稳定的概念层. 相比较而言,
css的选择符所提供的节点定位方式要比xpath更加简单直观, 它的适用性也早已在大量的实践中得到了证实.
基于css选择符实现behaviour机制是一种更加可行的方案. 参见 <a href="http://prototype.conio.net/">http://prototype.conio.net/</a><img src ="http://www.blogjava.net/canonical/aggbug/27196.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/canonical/" target="_blank">canonical</a> 2006-01-08 23:21 <a href="http://www.blogjava.net/canonical/archive/2006/01/08/27196.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[导入]UnitTest:语义校验</title><link>http://www.blogjava.net/canonical/archive/2005/12/28/25795.html</link><dc:creator>canonical</dc:creator><author>canonical</author><pubDate>Wed, 28 Dec 2005 14:34:00 GMT</pubDate><guid>http://www.blogjava.net/canonical/archive/2005/12/28/25795.html</guid><wfw:comment>http://www.blogjava.net/canonical/comments/25795.html</wfw:comment><comments>http://www.blogjava.net/canonical/archive/2005/12/28/25795.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/canonical/comments/commentRss/25795.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/canonical/services/trackbacks/25795.html</trackback:ping><description><![CDATA[<p>&nbsp;&nbsp;
单元测试随着agile的流行已经家喻户晓了，这正反映了软件的一个本质特征：软件是Man-Made的，而人是不可靠的。软件出错的高频率必然导致控制
间隔的缩短。我最早是在编写matlab程序的时候独立的发现了单元测试的作用。因为matlab是弱类型的，横纵矢量也不区分，很容易犯错误，我就为每
一个matlab函数编写了测试脚本，约定了命名规范为xxx_test.m, 在测试的时候通过
can_assert来做出判断而不是输出变量内容进行人工检查。
每次作了修改的时候，运行一下can_test_all.m就自动搜索测试脚本并运行一遍。 后来xp出现了，
我也第一次听说了单元测试这回事，看来英雄所见略同吧 ^_^<br>
&nbsp;&nbsp;
单元测试可以看作是对编译器的补充。编译器只能进行语法检查（形式），而单元测试可以进行语义检查(内容），它其实维护了程序的一个语义框架。如果单元测
试程序编写出来了，即使一行业务实现代码也没编写，我们手中也已经拥有了一笔宝贵的财富，因为程序的语义已经在某种意义下确定下来了。重构一般是在维护单
元测试不变的情况下进行的。即我们发现在维护语义不变量的情况下，系统具有相当的调整余地。<br>
&nbsp;&nbsp;
坚持单元测试的另一个好处是它倾向于良好分离的模块，因为高内聚低耦合的模块才更容易进行测试。为了测试，我们会在不知不觉中在对象职责的分离上投注更多
的心力。在witrix平台中，虽然基于EasyMock提供了mock支持，在实际中却很少使用，因为模块功能一般很独立，不需要复杂的mock对象，
而对于一般的对象，eclipse有代码生成功能，可以非常轻易的生成简单的测试对象。<br>
&nbsp;&nbsp; 虽然JUnit非常流行，我们的单元测试也是基于JUnit进行的，但是我们还是进行了一个简单的封装，将JUnit框架的特定要求与具体测试代码剥离开来。具体的，测试类从<br>
&nbsp;&nbsp; test.UnitTest继承，而不是从JUnit的TestCase继承。使用Debug.check()来做判断，而不是JUnit的assertEquals等。<br>
&nbsp;&nbsp; class MyTest extends UnitTest{<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public MyTest(String caseName){ super(caseName); }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void testMy(){<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MyObject my = new MyObject();<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Object value = myObject.myFunc();<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Debug.check(value.equals("aa"));<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 可以同时提供一个出错消息<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Debug.check(value.equals("aa"),"myFunc return invalid : "+value);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public static void main(String[] args){<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 不需要IDE或者其他外部的支持就可以直接调用测试代码，将会自动输出运行时间等<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; UnitTest.exec(new MyTest("testMy"));<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>
&nbsp;&nbsp; }<br>
</p>
				  
             <img src ="http://www.blogjava.net/canonical/aggbug/25795.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/canonical/" target="_blank">canonical</a> 2005-12-28 22:34 <a href="http://www.blogjava.net/canonical/archive/2005/12/28/25795.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>通过继承构造业务对象环境</title><link>http://www.blogjava.net/canonical/archive/2005/12/06/22796.html</link><dc:creator>canonical</dc:creator><author>canonical</author><pubDate>Tue, 06 Dec 2005 14:33:00 GMT</pubDate><guid>http://www.blogjava.net/canonical/archive/2005/12/06/22796.html</guid><wfw:comment>http://www.blogjava.net/canonical/comments/22796.html</wfw:comment><comments>http://www.blogjava.net/canonical/archive/2005/12/06/22796.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/canonical/comments/commentRss/22796.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/canonical/services/trackbacks/22796.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp; 现在很多设计中推崇接口和依赖注入(dependency
injection)，而不倾向于采用继承机制来构造程序结构。但很多时候作为一种简便而廉价的封装方法,继承仍然是不可或缺的.
例如与一些Engine打交道的时候,需要实现某些特定的接口. 在osworkflow中, 我们需要实现FunctionProvider接口, <br>
&nbsp;&nbsp;&nbsp;&nbsp; interface FunctionProvider{<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void execute(Map transientVars, Map args, PropertySet ps) throws WorkflowException;<br>
&nbsp;&nbsp;&nbsp;&nbsp; }<br>
&nbsp;&nbsp;&nbsp; 在Quartz中需要实现Job接口<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; interface Job{<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void
execute(JobExecutionContext context) throws JobExecutionException;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>
&nbsp;&nbsp;&nbsp; 这些接口是一种技术性的要求, 它们表示了代码生存所依赖的技术环境. 为了屏蔽这种对于外部引擎的依赖, 我们可以简单的选择实现一个基类,<br>
&nbsp;&nbsp;&nbsp; abstract class AbstractFunction implements FunctionProvider,Runnable{<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Map transientVars;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Map args;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PropertySet ps;<br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public final void execute(Map transientVars, Map args, PropertySet ps){<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.transientVars = transientVars;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.args = args;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.ps = ps;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; run();<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public Object getPersistVar(String name){<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return ps.getAsActualType(name);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void setPersistVar(String name, Object value){<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ps.setAsActualType(name,value);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void removePersistVar(String name){<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ps.remove(name);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>
&nbsp;&nbsp;&nbsp; }<br>
&nbsp;&nbsp;&nbsp;
在派生类中我们只要使用getPersistVar等函数就可以回避对于osworkflow特有的PropertySet类的依赖，而只在概念上需要一
个对象的持久化机制．当我们把业务代码从osworkflow移植到其他工作流引擎的时候，　只需要改变一下基类即可．我们可以在基类中引入更加特殊的假
设，<br>
&nbsp;&nbsp;&nbsp; abstract AbstractBusinessFunction extends AbstractFunction{<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public BusinessObject getBusinessObject(){<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return transientVars.get("businessObject");<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void commonBusinessOp(){ ... }<br>
&nbsp;&nbsp;&nbsp; }<br>
<br>
&nbsp;&nbsp;&nbsp;
AbstractBusinessFunction提供的可以是一个完整的业务对象环境，　我们在派生类中的所有代码都可以是与业务直接相关的，而与具体
的技术实现无关（例如业务变量是存放在transientVars中还是存放在args中）<br>
<br>
&nbsp;&nbsp;&nbsp; class BusinessFunction extends AbstractBusinessFunction{<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void run(){<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BusinessObject bo = getBusinessObject();<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bo.methodA();<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; commonBusinessOp();<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>
&nbsp;&nbsp;&nbsp; }<br>
&nbsp;&nbsp;&nbsp;
对于我们来说实际有意义的是在派生类中所书写的代码，基类仅仅提供一个环境而已．无论我们使用Ioc注入业务变量还是从transientVars中主动
获取业务变量，都是与我们的业务操作无关的．　实际上在理论上我们希望整个基类都可以是注入的（包括业务变量和业务操作），在泛型编程中这对应于所谓的
policy class. <br>
<br>
<img src ="http://www.blogjava.net/canonical/aggbug/22796.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/canonical/" target="_blank">canonical</a> 2005-12-06 22:33 <a href="http://www.blogjava.net/canonical/archive/2005/12/06/22796.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[导入]tag技术</title><link>http://www.blogjava.net/canonical/archive/2005/12/02/22301.html</link><dc:creator>canonical</dc:creator><author>canonical</author><pubDate>Fri, 02 Dec 2005 14:59:00 GMT</pubDate><guid>http://www.blogjava.net/canonical/archive/2005/12/02/22301.html</guid><wfw:comment>http://www.blogjava.net/canonical/comments/22301.html</wfw:comment><comments>http://www.blogjava.net/canonical/archive/2005/12/02/22301.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/canonical/comments/commentRss/22301.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/canonical/services/trackbacks/22301.html</trackback:ping><description><![CDATA[<p>&nbsp;&nbsp;&nbsp; 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语法，简直是自找麻烦。<br>
&nbsp;&nbsp;&nbsp;
这里最关键的地方就是自定义tag可以实现抽象层次的提升，是一种类似于函数封装的机制，从而实现概念的分离和明确化。基于tag可以实现页面元素的组件
化，加上xml语法的可理解性，表达能力以及无与伦比的集成能力，使得tag技术可以超越VB等组件开发环境（想想集成别人的组件代码难还是集成别人的
xml描述文件难）。自定义tag提供的抽象能力不仅仅是面向对象的，而且是类似AOP的，这些都极大的辅助了我们的思考和设计。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;cocoon使用管道技术也构造了某种tag机制，但是它的效率很成问题。从数学上说多个处理函数 g, h, k可以通过函数组合(composition)构成新的函数f</p>
<p>&nbsp;&nbsp;&nbsp; f(data) = g&nbsp;*&nbsp;h * k(data)&nbsp;</p>
<p>这是所谓函数式语言强大能力的源泉。cocoon处理的时候从k(data)开始，处理完毕之后调用h, 即函数是从右向左结合的。如果我们保证处理函数满足左结合律，则g*h*k就可以预编译为f, 从而解决性能问题，这正是witrix平台中tpl技术所采用的方案。</p>
<img src ="http://www.blogjava.net/canonical/aggbug/22301.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/canonical/" target="_blank">canonical</a> 2005-12-02 22:59 <a href="http://www.blogjava.net/canonical/archive/2005/12/02/22301.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title> AjaxAnywhere</title><link>http://www.blogjava.net/canonical/archive/2005/11/23/21184.html</link><dc:creator>canonical</dc:creator><author>canonical</author><pubDate>Wed, 23 Nov 2005 13:53:00 GMT</pubDate><guid>http://www.blogjava.net/canonical/archive/2005/11/23/21184.html</guid><wfw:comment>http://www.blogjava.net/canonical/comments/21184.html</wfw:comment><comments>http://www.blogjava.net/canonical/archive/2005/11/23/21184.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/canonical/comments/commentRss/21184.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/canonical/services/trackbacks/21184.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp; <a href="http://ajaxanywhere.sourceforge.net/index.html">http://ajaxanywhere.sourceforge.net/index.html</a><br>
&nbsp;&nbsp;&nbsp; AjaxAnywhere利用JSP标签把Web页面标注出可以动态装载的区域, 可以直接把任何JSP页面转化为AJAX感知组件而不需要进行复杂的Javascript编码.<br>
&nbsp;&nbsp;&nbsp; &lt;script&gt; ajaxAnywhere.getZonesToLoad = function(url){ return "countriesList"; } &lt;/script&gt;<br>
&nbsp;&nbsp;&nbsp; &lt;select size="10" name="language" onchange="ajaxAnywhere.submitAJAX();"&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;%@ include file="/locales_options_lang.jsp"%&gt;<br>
&nbsp;&nbsp;&nbsp; &lt;/select&gt;<br>
<br>
&nbsp;&nbsp;&nbsp; &lt;aa:zone name="countriesList"&gt;<br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;select size="10" name="country" &gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;%@ include file="/locales_options_countries.jsp"%&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/select&gt;<br>
<br>
&nbsp;&nbsp;&nbsp; &lt;/aa:zone&gt;<br>
&nbsp; &nbsp;<br>
&nbsp;&nbsp; AjaxAnywhere的这种做法与witrix平台中的ajax方案有些类似, 例如<br>
&nbsp;&nbsp;&nbsp; <br>
&nbsp;&nbsp;&nbsp;
&lt;select onchange="new
js.Ajax().setObjectEvent('changeLanguage').setParam(this).setTplPart('countriesList').replaceChildren('countriesList')"&gt;
...&lt;/select&gt;<br>
<br>
&nbsp;&nbsp;&nbsp; &lt;div id="countriesList"&gt;<br>
&nbsp;&nbsp; &lt;tpl:define id="countriesList"&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ....<br>
&nbsp;&nbsp; &lt;/tpl:define&gt;<br>
&nbsp;&nbsp;&nbsp; &lt;/div&gt;<br>
<br>
&nbsp;&nbsp;&nbsp;
但是在AjaxAnywhere的方案中, 后台jsp页面总是要完整运行的, 它通过servlet filter机制缓存所有的jsp输出,
而aa:zone标签则把自己的bodyContent运行后的结果保存在request的attribute中, 最后servlet
filter根据调用参数决定返回那些zone的运行结果. 而在witrix平台中的方案中, 只有指定的tplPart才会被运行,
其他部分完全被忽略. 这种差异的根源在于Jsp Tag技术本身的局限性. Jsp Tag的设计是非常原始的,
基本上就是在字符串层面上进行操作, 在运行的时候缺乏对页面结构强有力的控制. 实际上, 在我看来, 所有基于jsp tag的技术都受制于jsp
tag的先天的局限性, 很难有深度的发展, 包括JSF技术.<br>
&nbsp;<img src ="http://www.blogjava.net/canonical/aggbug/21184.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/canonical/" target="_blank">canonical</a> 2005-11-23 21:53 <a href="http://www.blogjava.net/canonical/archive/2005/11/23/21184.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[导入]Quartz 任务调度</title><link>http://www.blogjava.net/canonical/archive/2005/11/22/21011.html</link><dc:creator>canonical</dc:creator><author>canonical</author><pubDate>Tue, 22 Nov 2005 09:55:00 GMT</pubDate><guid>http://www.blogjava.net/canonical/archive/2005/11/22/21011.html</guid><wfw:comment>http://www.blogjava.net/canonical/comments/21011.html</wfw:comment><comments>http://www.blogjava.net/canonical/archive/2005/11/22/21011.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/canonical/comments/commentRss/21011.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/canonical/services/trackbacks/21011.html</trackback:ping><description><![CDATA[<p>quartz是一个高质量的任务调度软件包。其主要组成部分为:<br>
</p>
<p>Scheduler接口: quartz的执行线程，它根据Trigger决定调度时刻，根据JobDetail的说明实例化并运行Job<br>
</p>
<p>JobDetail类: 可持久化的任务描述信息。任务虽然分组，但是仅用作管理标示，任务之间并无实质性关联, 例如无法定义job chain。<br>
</p>
<p>Trigger类:任务的调度策略。这里的特点是调度策略与任务描述分开，调度策略和任务描述都可以分别在Scheduler注册，然后再关联起来。JobDetail与Trigger的关系是一对多。<br>
</p>
<p>JobDataMap: 将任务的运行时可持久化状态信息从JobDetail类中分离出来<br>
</p>
<p>Job接口: 任务的执行代码<br>
</p>
<p>StatefulJob接口: 无状态任务对应的JobDataMap可以认为是只读的,而有状态的任务在多次执行过程中保留对JobDataMap所作的修改,一个后果是有状态任务无法被并发执行。<br>
</p>
<p>JobExecutionException类: 可以通过JobExecutionException调整调度程序的下一步动作<br>
Calendar接口: 用于从trigger的调度计划中排除某些时间段，例如假期等。</p>
<p>以上几个部分的交互关系如下：<br>
class JobImpl implements Job{<br>
&nbsp;&nbsp;&nbsp; public void execute(JobExecutionContext context) throws JobExecutionException{<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; JobDetail detail = context.getJobDetail();<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; JobDataMap dataMap = detail.getJobDataMap();<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...<br>
&nbsp;&nbsp;&nbsp; }<br>
}</p>
<p>scheduler.addCalendar("myHolidays", holidayCalendar, false);<br>
trigger.setCanlendarName("myHolidays");</p>
<p>JobDetail jobDetail = new JobDetail(jobName, jobGroupName, JobImpl.class);</p>
<p>scheduler.scheduleJob(jobDetail, trigger);</p>
<p>JobDetail可以设置如下属性:<br>
1. Durability: non-durable的任务当不再与任何active trigger关联的时候将会从scheduler中被自动删除。<br>
2. Volatility: volatile的任务在scheduler的两次启动之间不会被持久化<br>
3. RequestsRecovery: 如果在执行过程中程序意外崩溃，标记为"request recovery"的任务在scheduler重起之后将会被再次执行，此时JobExecutionContext.isRecovering()返回true.</p>
<p>Trigger可以设置如下属性:<br>
1. misfireInstruction: 设定当trigger错过了触发时刻的时候需要采取的处理策略</p>
<p>SimpleTrigger按照固定的时间间隔进行触发<br>
startTime, endTime, repeatCount, repeatInterval</p>
<p>CronTrigger按照日历间隔进行触发<br>
seconds minutes hours day-of-month month day-of-week</p>
<p>在quartz内部，QuartzSchedulerThread按照时间顺序选择trigger（没有任务优先级的概念）, 然后在JobRunShell中运行Job。</p>
<p>JobRunShell中的调用顺序如下:</p>
<p>TriggerListener.triggerFired<br>
&nbsp;&nbsp;&nbsp; Called by the Scheduler when a Trigger has fired, and it's associated JobDetail is about to be executed.</p>
<p>TriggerListener.vetoJobExecution<br>
&nbsp;&nbsp;&nbsp; Called by the Scheduler when a Trigger has fired, and it's associated JobDetail is about to be executed.</p>
<p>JobListener.jobToBeExecuted<br>
&nbsp;&nbsp;&nbsp; Called by the Scheduler when a JobDetail is about to be executed (an associated Trigger has occured).</p>
<p>Job.execute<br>
&nbsp;&nbsp;&nbsp; Called by the Scheduler when a Trigger fires that is associated with the Job. <br>
&nbsp; <br>
JobListener.jobWasExecuted<br>
&nbsp;&nbsp;&nbsp; Called by the Scheduler after a JobDetail has been
executed, and be for the associated Trigger's triggered(xx) method has </p>
<p>been called.</p>
<p>Trigger.executionComplete<br>
&nbsp;&nbsp;&nbsp; Called after the Scheduler has executed the
JobDetail associated with the Trigger in order to get the final
instruction </p>
<p>code from the trigger. </p>
<p>TriggerListener.triggerComplete<br>
&nbsp;&nbsp;&nbsp;&nbsp; Called by the Scheduler when a Trigger has
fired, it's associated JobDetail has been executed, and it's
triggered(xx) </p>
<p>method has been called.</p>
SchedulerListener.triggerFinalized [if(trigger.getNextFireTime() == null)]<br>
&nbsp;&nbsp;&nbsp;&nbsp; Called by the Scheduler when a Trigger has reached the condition in which it will never fire again. <img src ="http://www.blogjava.net/canonical/aggbug/21011.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/canonical/" target="_blank">canonical</a> 2005-11-22 17:55 <a href="http://www.blogjava.net/canonical/archive/2005/11/22/21011.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>代码习惯</title><link>http://www.blogjava.net/canonical/archive/2005/11/17/20214.html</link><dc:creator>canonical</dc:creator><author>canonical</author><pubDate>Thu, 17 Nov 2005 03:55:00 GMT</pubDate><guid>http://www.blogjava.net/canonical/archive/2005/11/17/20214.html</guid><wfw:comment>http://www.blogjava.net/canonical/comments/20214.html</wfw:comment><comments>http://www.blogjava.net/canonical/archive/2005/11/17/20214.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/canonical/comments/commentRss/20214.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/canonical/services/trackbacks/20214.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp; 新手总是有很多不好的代码习惯. 最常见的一个是不使用临时变量.例如 <br>
&nbsp;&nbsp;&nbsp; for(int i=0;i&lt;myList.size();i++){<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; otherList.get(i).getSomeVar().getName();<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; otherList.get(i).getSomeVar().getValue();<br>
&nbsp;&nbsp;&nbsp; }<br>
&nbsp;&nbsp;&nbsp; 这种做法有如下后果:<br>
&nbsp;&nbsp;&nbsp; 1. 代码冗长, 容易出错, 例如循环体中的某个i写成了j<br>
&nbsp;&nbsp;&nbsp; 2. 函数调用终究是要耗费时间的, 在一个循环体中的调用往往对性能有可见的影响. 特别是当函数动态装载数据的时候, 例如每次调用该函数都查询数据库, 这种不使用临时变量的方法将会为系统留下性能隐患.<br>
&nbsp;&nbsp;&nbsp; 3. 一条很长的语句如果不是为流式调用而设计的, 则这种调用方式会影响到我们的调试工作. 例如
当某个中间步骤返回空指针时, 程序会抛出NullPointerException异常, 而我们得到的信息只是某一行存在空指针异常,
但是无法定位到具体是哪个步骤. 当某个中间步骤返回的值不是null但也不是我们所期望的值的时候, 我们同样难以诊断出具体出错的步骤.
使用临时变量将会为调试提供便利<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int i,n=myList.size();<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for(i=0;i&lt;n;i++){<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MyVar var = otherList.get(i);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var.getName();<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var.getValue();<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在需要的时候我们可以在出错语句处加上断点, 或者直接输出可疑的变量值.<br>
&nbsp;&nbsp;&nbsp; 4. 长语句不利于抽象出子函数. 例如在第二种方式中我们抽象出子函数的难度比第一种方式小<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void processVar(MyVar var){<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var.getName();<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var.getValue();<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>
<br>
&nbsp;&nbsp;&nbsp; 造成这些习惯的原因很耐人寻味, 我猜想缺乏抽象能力似乎是最基本的原因, 毕竟为变量起一个名字也是最简单的抽象步骤之一.<br>
<br>
<img src ="http://www.blogjava.net/canonical/aggbug/20214.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/canonical/" target="_blank">canonical</a> 2005-11-17 11:55 <a href="http://www.blogjava.net/canonical/archive/2005/11/17/20214.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[导入]EasyCalendar:一维随机游走</title><link>http://www.blogjava.net/canonical/archive/2005/11/16/20142.html</link><dc:creator>canonical</dc:creator><author>canonical</author><pubDate>Wed, 16 Nov 2005 11:28:00 GMT</pubDate><guid>http://www.blogjava.net/canonical/archive/2005/11/16/20142.html</guid><wfw:comment>http://www.blogjava.net/canonical/comments/20142.html</wfw:comment><comments>http://www.blogjava.net/canonical/archive/2005/11/16/20142.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/canonical/comments/commentRss/20142.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/canonical/services/trackbacks/20142.html</trackback:ping><description><![CDATA[<p><font color="#000000" size="3">在时间轴上定位一般比较麻烦，我们可以编写大量的get函数来得到特殊时间点，如
getFirstDayOfMonth(int year,int month), getFirstDayOfNextMonth(int
year, int month)，这不如采用如下正交化的流式设计。<br>EasyCalendar cal = </font></p>
<font color="#000000" size="3">&nbsp;&nbsp;&nbsp; new EasyCalendar().toYear(2001).toMonth(1).toFirstDayOfMonth().toFirstDayOfWeek();</font><img src ="http://www.blogjava.net/canonical/aggbug/20142.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/canonical/" target="_blank">canonical</a> 2005-11-16 19:28 <a href="http://www.blogjava.net/canonical/archive/2005/11/16/20142.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[导入]数据仓库建模</title><link>http://www.blogjava.net/canonical/archive/2005/11/15/19835.html</link><dc:creator>canonical</dc:creator><author>canonical</author><pubDate>Tue, 15 Nov 2005 04:29:00 GMT</pubDate><guid>http://www.blogjava.net/canonical/archive/2005/11/15/19835.html</guid><wfw:comment>http://www.blogjava.net/canonical/comments/19835.html</wfw:comment><comments>http://www.blogjava.net/canonical/archive/2005/11/15/19835.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/canonical/comments/commentRss/19835.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/canonical/services/trackbacks/19835.html</trackback:ping><description><![CDATA[<font color="#000000" size="3">关系数据库的关键之处在于关系的分解，在数据库中只定义了数据之间的两两关系，与应用相
关的更复杂的数据关系需要在运行时通过动态join来构造出来，即这些关系储存在程序中而不是数据库中。实际上，关系数据库的一个隐含的假定是数据之间很
少关联，而在实际应用中单表和主从表也正是最常出现的情况。当一个应用频繁需要大量表的连接操作的时候，往往意味着关系数据模型的失效，此时我们将不得不
放弃数据的无冗余性，需要通过预连接来构造实例化视图(Material View)，将数据之间的复杂关系固化并明确定义出来。
在数据仓库里，抽象的讨论star schema和snowflake schema哪个更优越是一个毫无意义的问题。
应该聚合到什么程度，需要根据数据应用的具体情况而定。<br>&nbsp;&nbsp;&nbsp;
关系数据库本身定义的是数据之间的两两关系，缺乏一些全局数据访问手段。而数据仓库的一个基本概念是数据空间，即可以通过全局坐标来直接访问数据，而不是
通过两两连接来访问数据。在数据仓库中最重要的就是时间维度，因为这是所有数据所共享的一个坐标维度。我们可以将两个发生在同一时间点上的数据直接并列在
一起，而无论它们之间是否定义了关联(relation)。<br>&nbsp;关系数据库的基本数据访问模式如下：<br>&nbsp;select 属性列表&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;from 表A, 表B<br>&nbsp;where 表A.data_id = 表B.id<br>&nbsp;and 表B.attr = 'A'<br>&nbsp;在数据仓库中 " from 表A, 表B where 表A.data_id = 表B.id "这一部分将多个多个数据表和表之间的关联条件放在一起定义为所谓的主题。<br>&nbsp;而 表B.attr = 'A' 这一部分就从where子句中分离出来作为坐标条件。<br>&nbsp;&nbsp;&nbsp; 在数据仓库中建立时间坐标有两种方式，对于发生在时间点上的事件我们直接建立点坐标，通过his_date字段来表示，而对于延续一段时间的状态数据，我们可以建立区间坐标，通过from_date和to_date两个字段来表示。</font><img src ="http://www.blogjava.net/canonical/aggbug/19835.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/canonical/" target="_blank">canonical</a> 2005-11-15 12:29 <a href="http://www.blogjava.net/canonical/archive/2005/11/15/19835.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[导入]xml db vs RDB</title><link>http://www.blogjava.net/canonical/archive/2005/11/15/19831.html</link><dc:creator>canonical</dc:creator><author>canonical</author><pubDate>Tue, 15 Nov 2005 04:23:00 GMT</pubDate><guid>http://www.blogjava.net/canonical/archive/2005/11/15/19831.html</guid><wfw:comment>http://www.blogjava.net/canonical/comments/19831.html</wfw:comment><comments>http://www.blogjava.net/canonical/archive/2005/11/15/19831.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/canonical/comments/commentRss/19831.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/canonical/services/trackbacks/19831.html</trackback:ping><description><![CDATA[&nbsp;&nbsp; 关系数据库的理论基础是集合论，而集合的基本定义就是不重复的一组元素。而xml数据库方面尚缺乏相应的理论来消除数据冗余性。<br>
&nbsp;&nbsp;
关系数据库能够成功的另外一个重要原因是它采用平面表形式，而应用中大量使用的正是平面表，所以数据库表在很多时候是数据的最适表现形式，使用xml表达
只会增加不必要的复杂性。平面表的基本假设是所有条目的结构都是一样的（具有一个header），而xml表示形式本身不存在这样的假定，因此很多时候无
法根据数据的shape来做有效的优化。当然xml
schema等技术正在快速发展的过程中，当相应的元数据描述和使用技术逐渐成熟之后，xml的处理方式会得到本质的提高。<br>
&nbsp;&nbsp; xml技术是目前元语言的代表，它最重要的技术优势在于它是人与机器都能轻易理解的语言，是人机共享的信道!
目前它并不适合在应用程序中表达复杂的多维关联。特别是目前多数操纵xml的API都是面向文档的，所存取的数据类型都是字符串，更造成了程序应用上的困
难。<br>
<br>
<img src ="http://www.blogjava.net/canonical/aggbug/19831.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/canonical/" target="_blank">canonical</a> 2005-11-15 12:23 <a href="http://www.blogjava.net/canonical/archive/2005/11/15/19831.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[导入]jsp tag的七宗罪</title><link>http://www.blogjava.net/canonical/archive/2005/11/15/19830.html</link><dc:creator>canonical</dc:creator><author>canonical</author><pubDate>Tue, 15 Nov 2005 04:21:00 GMT</pubDate><guid>http://www.blogjava.net/canonical/archive/2005/11/15/19830.html</guid><wfw:comment>http://www.blogjava.net/canonical/comments/19830.html</wfw:comment><comments>http://www.blogjava.net/canonical/archive/2005/11/15/19830.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/canonical/comments/commentRss/19830.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/canonical/services/trackbacks/19830.html</trackback:ping><description><![CDATA[一个技术的成功，在于最终占据了某个概念。当我们应用到此概念的时候，首先想到的就是这个技术实现，久而久之，形成一个自我证明的过程。而有些技术却是在
其位无能谋其政，实在是让人不得不为它扼腕叹息呀。jsp tag正是这样一种技术。有些人认为jsp
tag只是jsp的一种扩展，只是一种syntax suger, 这正反映了此技术所面临的一种困境。这里指出一些jsp
tag的设计缺陷，并无贬低这种技术的意图，只是希望抛砖引玉，引发大家对这种技术改进的探讨。 <br>
引用: <br>
jsp tag是服务器端的扩展标签机制，它是一系列java服务器端技术的基础。但其设计之初的几个重大缺陷已经使得这种技术不堪重负。 &nbsp;<br>
<br>
对比dotNet平台我们可以知道，需要一种后台标签机制，jsp
tag是唯一的标准（JSF等机制都依赖于此)，可它的设计给所有希望基于它开发的开发人员造成了巨大的困扰。实际上我对这个技术感到很失望，当然我们提
出了相应的替代方案。在我们的开发框架中使用的是重新设计的一套与网络无关的xml动态标签机制。我的观点是基于jsp
tag技术，无法开发出与dotNet的便捷程度相当的服务端技术，所以这是它作为标准的罪过。jsp
tag不应该是jsp的补充，它搭上了xml这条大船，应该去走自己的路，而不应该成为应用上的鸡肋。 <br>
引用: <br>
1. jsp tag与jsp 模型紧密绑定，使得业务逻辑很难抽象到tag中。而且脱离了jsp环境，在jsp tag上的技术投资将一无是处。 &nbsp;<br>
<br>
这里说业务逻辑可能是有些不妥，容易引起误解。因为我的工作做在中间件层，所以我的原意是基于jsp
tag开发一系列的扩展技术，比如缓存等。我们的xml标签技术是与jsp模型无关的，在前台用于界面渲染，在后台用于工作流描述。而且很方便的就可以与
其它xml技术结合，比如集成ant。 <br>
<br>
引用: <br>
2. jsp tag的编码逻辑非常繁琐, 特别是写loop和容器类标签的时候。在2.0之前不支持从tag本身产生tag更是应用上的主要障碍。 &nbsp;<br>
<br>
这绝对是个重大问题，试问多少人自己去开发jsp tag呢，多半是用用别人的成品。编制困难其实就是否定了界面元素的重用。很多人推崇Tapestry, 其实如果jsp tag技术方便一点，何必建立一个完全不同的模型呢。 <br>
<br>
引用: <br>
3. 使用将xml标签的属性直接映射到对象属性的方法造成tag对象是有状态的，不得不通过丑陋的pool机制来解决性能问题。 <br>
而且性能问题直接限制了大量小标签的使用。 &nbsp;<br>
<br>
这是一个现实的困难，一个系统设计师必须考虑。 <br>
<br>
引用: <br>
4. jsp tag是一种完全动态化的设计，大量可以在编译期进行的优化都无法进行，基本上所有的运算都是在运行期发生，限制了性能的提高。 &nbsp;<br>
<br>
我们的xml标签技术是先编译再运行的，加上无状态设计，在性能上可以得到控制。我的朋友hotman_x是个C++和js高手，在他的强烈要求下，我们的xml标签还增加了一个多次编译的机制。 <br>
<br>
引用: <br>
<br>
5. 虽然最近的版本已经支持xml格式，但对于xslt等的集成很不到位，例如现在无法使用xslt对jsp文件进行界面布局。 <br>
<br>
最简单的 <br>
&lt;web:template src="test.tpl" xslt="layout.xslt" /&gt; <br>
&lt;web:mytag xdecorator="face.xslt"&gt; <br>
... <br>
&lt;/web:mytag&gt; <br>
<br>
引用: <br>
6. jsp tag要求使用自定义标签名，而无法对已经存在的html标签进行enhance, 造成无法方便的在可视化编辑器中进行编辑。 &nbsp;<br>
<br>
Tapestry就认为它比较好。我们的xml标签机制也支持属性扩展。 <br>
<br>
引用: <br>
7. EL表达式竟然不支持调用对象函数 <br>
<br>
很多人因此觉得OGNL比较好。我们的机制中是对EL做了一定的增强。我不喜欢OGNL, 过于复杂了。<br>
<br>
<img src ="http://www.blogjava.net/canonical/aggbug/19830.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/canonical/" target="_blank">canonical</a> 2005-11-15 12:21 <a href="http://www.blogjava.net/canonical/archive/2005/11/15/19830.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[导入]JMX技术</title><link>http://www.blogjava.net/canonical/archive/2005/11/14/19738.html</link><dc:creator>canonical</dc:creator><author>canonical</author><pubDate>Mon, 14 Nov 2005 09:01:00 GMT</pubDate><guid>http://www.blogjava.net/canonical/archive/2005/11/14/19738.html</guid><wfw:comment>http://www.blogjava.net/canonical/comments/19738.html</wfw:comment><comments>http://www.blogjava.net/canonical/archive/2005/11/14/19738.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/canonical/comments/commentRss/19738.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/canonical/services/trackbacks/19738.html</trackback:ping><description><![CDATA[JMX在技术上的需求可以说是将管理功能从功能性接口中分离出来。<br>
例如一个缓存接口<br>
interface ICache{<br>
&nbsp;&nbsp;&nbsp;&nbsp; Object get(Object key);<br>
&nbsp;&nbsp;&nbsp;&nbsp; void put(Object key, Object value);<br>
}<br>
但一个具体实现类可能有很多参数可以调整，如缓存的最大尺寸等。这些可配置参数一般与具体实现紧密相关，即与实例相关，而不直接涉及到所要实现的功能。例如实现类可以具有setMaxSize()和getMaxSize()方法。<br>
如
果这些配置方法在功能接口中定义，就会造成功能接口的臃肿和不必要的与实现方法之间的依赖。如果直接调用实现类的方法，只能使用reflection，
但是java class作为元数据所承载的信息量有限，需要外部定义一个规范来补充信息。JMX就是这样的一种规范。<img src ="http://www.blogjava.net/canonical/aggbug/19738.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/canonical/" target="_blank">canonical</a> 2005-11-14 17:01 <a href="http://www.blogjava.net/canonical/archive/2005/11/14/19738.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>