Tuesday, July 31, 2007

Ajax -- 困惑者指南,第 1 部分: Ajax 工具和技术综述

级别: 初级

Gal Shachor (shachor@il.ibm.com), 高级技术职员,
IBM

Yoav Rubin (yoav@il.ibm.com), 软件工程师,
IBM

Shmulik London (shmulikl@il.ibm.com), 研究人员,
IBM

Shmuel Kallner (kallner@il.ibm.com), 软件工程师,
IBM

2007 年 7 月 23 日

Ajax (Asynchronous JavaScript + XML) 编程技术正逐渐主导 Web 应用程序开发领域。每天都有新的开发人员步入 Ajax 开发的殿堂,他们拥有不同的开发背景。本系列的第 1 部分将提供来自 IBM® Ajax 开发人员专家组的 Ajax 开发资源备忘单。作者根据他们自己的 ramp-up 经验为您提供了一些实用的信息,这些信息可以帮助您快速进入高效 Ajax 开发的轨道。




Ajax 是一种 Web 应用程序客户机技术,它结合了 JavaScript、层叠样式表(Cascading
Style Sheets,CSS)、HTML、XMLHttpRequest 对象和文档对象模型(Document Object Model,DOM),关于各种标准的更多信息,请参阅 参考资料。运行在浏览器上的 Ajax 应用程序以一种异步的方式与 Web 服务器通信,并且只更新页面的一部分。通过利用 Ajax 技术,可以提供丰富的、基于浏览器的用户体验。


在开始 Ajax 之旅以前,需要熟悉很多不同的语言、标准、工具和库。一开始就要在不同选项之间做出选择并组合出一个开发环境来可能有些勉为其难。作者将根据他们自己的 ramp-up(项目初始阶段的准备工作)经验帮助您在旅途中避免可能遇到的一些比较常见的、令人厌烦的问题。



developerWorks Ajax 资源中心

请访问 Ajax 资源中心,这里几乎囊括了关于 Ajax 编程模型的所有信息,包括各种文章和教程、论坛、博客、wikis、活动和新闻。

踏上 Ajax 的旅程


Ajax 将改变用户在网站上的体验,之前的单击-显示页面式的体验将变成具有交互性、灵活性的桌面应用程序体验。用户可以避免页面重新加载,避免等待下一部分内容的呈现。您可能在想,这一切听起来是不错,但是如何才能做得到呢?为了回答这个问题,先来看看 Ajax 技术背后的一些幕后故事。


Ajax 是以下 Web 技术和标准的集合,它允许使用客户端的机器在背后运行应用程序逻辑的重要部分,而不仅仅是呈现应用程序的输出:


  • 超文本标记语言(Hypertext Markup Language,HTML):定义最终呈现给用户的内容。

  • 层叠样式表(Cascading Style Sheets,CSS):定义所呈现内容的样式(或装饰)。

  • 文档对象模型(Document Object Model,DOM):一种 API,浏览器使用它将被呈现内容公开给 JavaScript 脚本。然后,脚本可以使用 DOM 在用户看到内容之前动态地修改内容。


  • XmlHttpRequest 对象:为浏览器与服务器之间的交互提供便利,通过 JavaScript 脚本调用。

  • JavaScript:在浏览器中执行的一种编程语言,将所有其他的 Ajax 组成部分黏合在一起。脚本可以侦听浏览器中发生的事件(例如单击一个按钮),并使用 XmlHttpRequests 回调服务器以对事件作出反应,然后根据返回的结果修改 DOM 树。



所有这些技术都在用户的 Web 浏览器中执行,用户的 Web 浏览器充当执行 Ajax 程序的平台。


如果要使用 Ajax,那么有一点值得格外注意:组成它的技术虽然是基于标准的,但又是特定于浏览器的。换句话说,同一个应用程序在不同的浏览器上可能表现出不同的行为。然而,由于既不可能限制用户使用特定的浏览器,又不可能忽略客户机的浏览器可能不支持 CSS 或 DOM 这一事实,因此,作为应用程序的创建者,您需要理解各种不同浏览器之间的差异。


要成为一名高效的 Ajax 开发人员,需要:


  • 了解形成 Ajax 开发基础的技术。

  • 理解可用的运行时平台和 Web 浏览器。

  • 开发 Ajax 应用程序时使用支持各种语言的 IDE。

  • 拥有用于各种环境和语言的调试器和应用程序测试工具。



信息源


developerWorks 上由 Brett McLaughlin 撰写的
掌握 Ajax

系列是最好的 Ajax 教程之一。该系列不要求预先知道多少 Ajax 编程知识,并且描述了各种与 Ajax 相关的技术。如果您想更好地理解 Web 架构,那么可能需要对 Representational State Transfer (REST) 有所熟悉。“How to Create a REST Protocol”(请参阅 参考资料)这篇文章可以作为这一方面很好的入门资料。


阅读完
掌握 Ajax
系列,并且亲自开始一些开发之后,您还将需要一些其他的参考信息。W3Schools(请参阅 参考资料)提供了核心 Ajax 技术(JavaScript、CSS、HTML、DOM、XML 等)的在线参考信息。


如果您使用的是 Mozilla Firefox 浏览器,那么应该使用 DevEdge 侧栏(请参阅 参考资料)。这些侧栏包含各种 Ajax 技术标准的索引,您可以使用它们作为参考指南。


最后,如果您想到 Ajax 社区去感受一下,那么应该订阅 Ajaxian 博客(请参阅 参考资料)。


浏览器作为运行时环境


Web 浏览器不仅是 Ajax 部署环境,同时还是调试环境。大多数浏览器都可以添加调试辅助工具,例如 DOM 树查看器或调试器。文章下节将对以下各种流行的浏览器作一个概述:


  • Microsoft® Internet Explorer (IE)

  • Firefox

  • Safari



Internet Explorer


IE 一直是大多数用户的选择。IE6 是一款成熟的浏览器,具有非常深的市场底蕴。很多 CSS 和 DOM 特性在 IE6 中不受支持,或者以一种专用的形式提供。使用 IE6 的开发人员可能会遇到以下一些问题:


  • 缓慢的 JavaScript 解释器和不一致的标准实现。

  • 缺少透明的 PNG 支持。


  • DOM 事件:一个事件可能在 Firefox 中使用一个名称,而在 IE 中又使用另一个名称。有些事件的调用顺序不一样,在 IE 中和在 Firefox 中相同的事件对象可能具有不同的属性。有关这一问题的更多信息,请阅读 “把应用程序从 Internet Explorer 迁移到 Mozilla”(请参阅 参考资料)。

  • 未实现的 CSS 属性,例如 minimum/maximum height、selectors 等等。




尽管存在这些问题,您还是应该相信大多数用户都是使用这个浏览器。也可以试着使用各种不同的库从而在一定程度上填补 IE6 的不足,例如 /IE7/(请参阅 参考资料)。


IE7 在 IE6 的基础上有了很多明显的改进,例如对标签的支持。对于开发人员而言,隐藏的改进更为重要。IE7 提供了一个性能更佳的 JavaScript 解释器,并修复了 IE6 中的很多缺陷(例如 CSS),使得最遵从标准的 Ajax 应用程序只需要少量的修改便可以在该浏览器上运行。


IE 调试辅助工具


可以使用以下工具在 IE 中调试应用程序:



  • IE Developer Toolbar:允许更好地控制 IE 环境(缓存,cookie),浏览 DOM 树,以及浏览和操纵元素的样式。在分析由应用程序生成的页面时,这是非常有用的工具。(IE Developer Toolbar 可以从 Microsoft 下载。参考资料 中提供了下载链接。)


  • Microsoft Script Debugger: 随 Microsoft Office(可以从 Microsoft 下载,请参阅 参考资料)一起发布,通过它可以在 JavaScript 代码中设置断点,观察变量,还可以执行常规的调试例程。注意,在调试时,建议在每个调试会话之前通过 IE Developer Toolbar 禁用缓存,否则后面会调试缓存版本的代码。



IE 相关浏览器



可以使用 Trident(IE 浏览器引擎)创建 “新” 的浏览器,例如 Maxthon。这里新字打上了引号,因为从开发人员的角度来看,浏览器的关键特(例如对 Ajax 的支持)仍然相同。(请参阅 参考资料 了解更多关于 Trident 的知识。)



警告:浏览器不是密封的


浏览器正逐渐成为增件的目标。用户可以下载和安装弹出窗口拦截器、标签管理器、UI 主题和其它增件。很多这样的增件会以一种出人意料、非常难于重现和调试的方式影响 Ajax 程序。一个简单的例子是,用户设置一个弹出窗口拦截器来拦截通过一个应用程序打开的新的浏览器窗口。这看起来是个无害的增件,但是,却令人难以一时觉察其对应用程序的影响,例如消耗浏览器窗口资源,以及隐藏应用程序的某些部分。

Mozilla Firefox


虽然 Firefox 已经出来好几个版本,但是这里只讨论 Firefox 2.0。开发人员和超级用户常常使用 Firefox,它是 UNIX®-类操作系统的首选,而且是开放源码的,除了 IE 之外,它是最流行的浏览器。


Firefox 有一个速度很快的 JavaScript 解释器,并且具有各种 Web 标准的优异的实现。由于它的 Ajax 友好性,我们强烈建议将 Firefox 用于大多数 Ajax 开发。通常,很多 Ajax 开发人员都是在 Firefox 上开始应用程序开发的,然后才转而使用其它浏览器。


Firefox 调试辅助工具



Firefox 具有一个强大的、开放的扩展机制,它的社区借助这种机制创建了大量的 Web 开发辅助工具。然而,到目前为止,Firebug(请参阅 参考资料)是其中最为流行的。(要了解关于 Firebug 功能的更多细节,请参阅 “Ajax Debugging with Firebug”)。Firebug 是一个集成的扩展,通过它可以:


  • 使用断点、变量查看和逐步方式来调试 JavaScript 代码。

  • 查看网络状况。

  • 查看和优化各种不同元素的 DOM 树和 CSS。



Firebug 1.0 的强大性和灵活性为 Ajax 开发树立了里程碑,也是使用 Firefox 作为首选开发浏览器的有力的理由。


Firefox 相关的浏览器




一些浏览器,例如 Camino,使用了 Firefox 的布局引擎 Mozilla Gecko(请参阅 参考资料)。这些浏览器通过一个匹配的 Gecko 版本能够提供与 Firefox 相同的功能。


Safari


Safari 是随 MacOSX 一起发布的浏览器(从第 3 版开始,也在 Windows® 上运行。)Safari 虽然比 Firefox 和 IE7 快很多,但是缺少对标准的支持(不过最新的 WebKit 构建有了一些改进;文章下节将会更详细地介绍 WebKit)。还应注意,Safari 开发人员社区规模远远小于 Firefox 和 IE 的开发人员社区。因此,网上关于 Safari 的信息较少,而且很多库都没有在 Safari 上经过良好的测试。


Safari 调试辅助工具



Safari 浏览器引擎是在一个名为 WebKit 的开放源码项目(请参阅 参考资料)中开发的。为了在 Safari 中调试应用程序,建议使用一个 WebKit 构建。该构建包括:


  • 经过定制的带有 CSS 和 DOM 查看器的浏览器。

  • Drosera: 一个 JavaScript 调试器。Drosera 可用于调试应用程序,但是与 Firebug 相比其特性比较有限。




如果您坚持在 Safari 中测试应用程序,那么可以打开 Safari Debug 菜单,通过该菜单可以查看 DOM 树、CSS 和缓存等。


Safari 相关的浏览器


WebKit 应用于很多浏览器,其中最引入注目的是 OmniWeb。


Ajax IDE 和工具


在开发新的 Ajax 应用程序时,如果所使用的 IDE 支持所有 Ajax 相关技术并且提供快捷、友好的开发环境和 UI 将会使您如虎添翼。在选择一款 Ajax IDE 时,需要考虑以下一些特性:


  • 具有自动完成、颜色突出显示、错误提示等功能的语法制导编辑功能(用于 HTML、CSS、JavaScript 和 DOM)

  • 各种技术的相关文档

  • 集成的 JavaScript 调试器

  • 网络监控功能

  • DOM 内省

  • 框架集成



虽然您可能会考虑购买一个 IDE,但其实 Eclipse 和它的相关插件就提供了一个功能完善的、基于开放源码的 Ajax IDE,这个 IDE 可以免费下载。本节将讨论以下这些基于 Eclipse 的、包括插件的 Ajax IDE:


  • 含 Web 工具箱(toolkit)的 Eclipse

  • Eclipse ATF

  • Aptana




本节还将讨论 JsUnit 单元测试包。


含有 Web 工具箱的 Eclipse



如果您是一名 Java™ 开发人员,负责服务器端的 Java 开发,那么使用 Eclipse Web 工具箱也是开始基本 Ajax 开发的简单易行的方法。通过添加 Eclipse 的 Web 工具(请参阅 参考资料),可以获得一个可用于开发 Web 工件的基本环境,还可以获得可编辑 HTML、CSS 和 JavaScript 文件的编辑器和编辑工具。



注意,Eclipse Web 工具环境不限于 Ajax 开发,它还支持侧重于服务器端的 Web 应用程序开发。因此,它提供了诸如语法突出显示和基本代码完成之类的核心功能。它并未提供特定于 Ajax 开发任务的调试功能或 DOM 操纵功能。


Eclipse ATF


您可以安装 Eclipse Ajax Toolkit Framework (ATF) 环境(请参阅 参考资料),并将其与 Eclipse Web 工具箱相结合,从而产生一个强大的、支持 Ajax 开发需求所有方面的环境。ATF 提供了以下特性:


  • 一个 DOM 浏览器和 CSS 运行时编辑器

  • 一个 JavaScript 调试器(通过利用 Mozilla XUL 运行程序)

  • 网络流量监控

  • 通过库配置文件实现常用库的简单库集成

  • 与 JSLint(请参阅 参考资料)的集成,以执行 JavaScript 验证



该工具箱中一个非常有价值的特性是在集成的 XUL 运行程序中运行、测试和检查应用程序。


Aptana


Aptana(请参阅 参考资料)是一个非常有用的、开放源码的、基于 Eclipse 的环境,并且可以免费下载。ATF 的工具专门针对 Ajax 开发,而 Aptana 则不同,它是用于开发 Web 2.0 应用程序的 IDE。Aptana 已经集成了 Ruby Development Toolkit (RDT),后者提供了 Ruby-Eclipse 集成。


可以下载 Aptana 作为一个完整的 Eclipse 环境(与 ATF 选项相比非常简单),也可以将其作为一组插件集添加到之前设置好的 Eclipse 环境中。



Aptana 环境提供了一些有用的特性,例如:


  • 高质量的用户指南和技术参考文档

  • 用于 Ajax 工件的一个完整的编辑环境,其中包含 JavaScript 代码分析、DOM 集成和浏览器兼容性信息

  • 使用外部 Firefox 浏览器的调试功能

  • 基本的 Ajax 库集成

  • 能标记潜在错误的代码验证系统

  • 编辑文件系统上的任何文件,和使用 FTP 编辑远程文件的功能



您也许会发现,Aptana 是比 ATF 更易于安装和学习的、能满足您的开发需求的解决方案。


JsUnit


JsUnit(请参阅 参考资料) 是基于 JavaScript 的单元测试框架,是通过将 JUnit(一个常见的 Java 单元测试库)移植到 JavaScript 创建而成的。JsUnit 支持用于 JavaScript 函数的测试用例的定义,并支持在浏览器环境中运行这些测试用例。如果您想对所开发的代码执行一致的单元测试,那么 JsUnit 是一款非常有用的框架。


Ajax 框架


最近出现了很多 Ajax 开发框架,它们的目标是使繁琐的 Ajax 开发任务变得更加容易。本节将讨论一些新出现的开放源码 Ajax 框架,这些 Ajax 框架通过以下方式使开发工作变得更加容易:


  • 提供了一组小部件(widget),可作为丰富 Web 用户界面(UI)的构建块。

  • 简化异步数据交互和传输协议。

  • 缓解任何跨浏览器的不兼容性。

  • 提供有用的 JavaScript 方法,以简化很多常见的 Ajax 开发任务,例如 DOM 操纵和事件注册。




讨论各种可用的 Ajax 框架超出了本文的范围,因此我们选择只详细讨论 Prototype、script.aculo.us、Dojo 和 Rico。要获得更完整的可用 Ajax 框架列表和了解它们的使用,请参阅 Ajaxian.com 2006 survey(参考资料 中提供了链接)。


Prototype


Prototype(请参阅 参考资料) 是一个 JavaScript 库,它引入了一些强大的库来帮助简化 Ajax 编程。虽然它本身不能看作一个 Ajax 框架,但是它是其它几种框架(例如 script.aculo.us、Moo.fx2、Rico 等)的核心。
Prototype 支持:


  • 基本 DOM 操作

  • Ajax 调用

  • 一组实用函数

  • 使用输入字段和表单




Prototype 是 Sam Stephenson 获得 Ruby 编程风格的灵感后开发的,它目前是麻省理工学院(Massachusetts Institute of Technology,MIT)许可下的一个开放源码项目(请参阅 参考资料)。


示例 Prototype 场景




下面是一个示例场景,它演示了如何使用 Prototype 简化 Ajax 编程。假设需要定期刷新一个页面中的某块区域,用于显示最新的新闻。于是可以设置一个计时器,每隔 1 分钟调用一次 updateNews() 方法。在 updateNews() 方法中,我们使用 Prototype Ajax.Updater 对最新新闻内容发起异步请求。接收到请求的结果后,再将其作为 div 元素 most-recent-news 中的 HTML 内容插入页面。清单 1 展示了 Prototype 所提供的众多强大的函数:



清单 1. 示例 Prototype 场景


<html>
<head>
<script src="js/prototype.js" type="text/javascript"></script>
<script type="text/javascript">
function updateNews() {
new Ajax.Updater(
{success: 'most-recent-news'},
'http://myserver/news/recent-news'
);
setTimeout("updateNews()",60000);
}
</script>
</head>
<body onload="updateNews()">
<h1>Recent News</h1>
<div id="most-recent-news"></div>
</body>
</html>


如果不使用 Prototype,那么这个示例就会变得更冗长,需要花更大的精力编写代码。


Script.aculo.us


Script.aculo.us(请参阅 参考资料)是 MIT 许可下的一个开放源码项目。它构建于 Prototype 之上,随 Ruby on Rails 一起发布。Script.aculo.us 是用于开发 rich Web-UI Rails 应用程序的一款流行的框架,但是也可以将它与其它服务器端技术一起使用。Script.aculo.us 虽然被描述为一个瘦框架,但是却拥有非常强大的动画功能,并且包含以下特性:


  • 基于 JavaScript 的 DOM 构建程序可以提供简单的 DOM 操作

  • 拖放支持

  • 一些控件和小部件,例如自动完成、滑动块和就地编辑




清单 2 中的示例展示了 Script.aculo.us 中强大的动画效果。在这个例子中,当点击一行文本中时,就会逐渐淡出视线:



清单 2. Script.aculo.us 动画效果


<script src="js/prototype.js" type="text/javascript"></script>
<script src="js/scriptaculous.js" type="text/javascript"></script>
...
<div onclick="new Effect.Fade(this)">
Click here to see me fade out!
</div></html>


Dojo Toolkit


Dojo Toolkit(请参阅 参考资料)是一款全面的 Ajax 框架,能满足多种需求。Dojo 提供:


  • 用于 JavaScript 的跨浏览器的 stdlib

  • DOM 操作

  • 用于优化下载时间和大小的打包系统

  • 一组丰富的、可扩展的小部件

  • 拖放支持

  • 动画支持

  • 非常丰富的服务器端集成库



Dojo 的一个卖点是,它的小部件是可扩展的,这样就可以利用简单的小部件组合出更复杂的小部件。因此,与大多数 Ajax 库和框架不同的是,Dojo 提供了一种特定的编程模型指导功能,如果遵从该模型,就可以像桌面 GUI 编程那样设计 Web 界面。



Dojo 是 Academic Free License 下的一个开放源码项目,正处于 Dojo Foundation 的开发之中的(请参阅 参考资料)。Dojo 是一个非常活跃的项目,很多大公司,例如 IBM、Sun Microsystems、AOL 等都提供了对 Dojo 的支持。


图 1 展示了一个使用 Dojo 的邮件应用程序的示例。可以看到,Dojo 提供了树、表和很多其它小部件,以帮助创建类似桌面的 UI。它还简化了这些小部件之间的交互。




图 1. 示例 Dojo 界面

示例 Dojo 界面


Rico 框架


Rico 是 Apache 2.0 许可下提供的一个开放源码 Ajax 框架(请参阅 参考资料 访问 Rico Web 站点并获得对 Apache 2.0 许可的描述)。它极具价值,但所占空间却很小。Rico 最突出的特性有:


  • 丰富的使用模板的 DOM 操作

  • 各种小部件,例如折叠(accordion)、LiveGrid 和下拉(pull down)

  • 拖放支持

  • 简单的动画

  • 用于圆角、管理颜色和其它显示的实用工具

  • 简单的服务器端集成



虽然 Rico 包括的函数比 Dojo 框架少,但是如果可用的函数能满足 UI 开发需求,那么 Rico 很小的内存占有量就是一个优势。尤其是,Rico 的 LiveGrid 小部件使得创建动态表之类的常见任务变得非常简单。


初学者的路线图 —— 现在就出发


如果您已经准备好开始 Ajax 开发之旅,那么下面的路线图可以告诉您开始的步骤。(注意,这个路线图使用 Aptana 作为 IDE,并使用 Dojo 作为 Ajax 框架,但是也可以改变配置,以适合您自己的需求。)


  • 下载 Mozilla Firefox 2.0,并安装 Firebug 插件。

  • 下载并安装 Aptana。

  • 遵循 “Mastering Ajax” 教程,在 Aptana 中编写代码示例。在 Firebug 中对例子进行调试。

  • 下载 Dojo Toolkit,并通读 Dojo “Hello World” 教程(请参阅 参考资料)。



结束语和下一步计划



至此,您可能有太多的 Ajax 应用程序开发工具信息需要慢慢消化,并且,如果您遵循了前面提到的初学者路线,那么也有了一些 Ajax 应用程序方面的实践和经验了。那么接下来干什么?深入阅读 Ajax 相关技术方面的资料,选择性地阅读关于要使用的 Ajax 框架的信息,这样可以帮助您夯实知识基础。这里有很多关于 HTML、JavaScript 和 CSS 的资料(参考资料中有 W3School 教程的链接,这些教程是很好的入门教程)。您还应该访问 Ajax Patterns 站点(请参阅 参考资料)。入门的最快方法是现在就动手 —— 自己编写一个应用程序试试。


本系列的下一篇文章将通过开发一个简单的基于 Dojo 的博客阅读器,指导您将新学到的 Ajax 开发知识应用于实践。还没有踏上 Ajax 的旅程?很快的。继续吧!




参考资料

学习

获得产品和技术
  • Microsoft Download Center 下载 IE Developer Toolbar 和 Microsoft Script Debugger。

  • 通过添加 Eclipse 的 Web 工具 可以得到一个可用的开发 Web 工件的基本环境,以及 HTML、CSS 和 JavaScript 文件的编辑器和编辑工具。

  • 进一步了解并下载 Eclipse Ajax Toolkit Framework (ATF)


  • Aptana 是一个有用的开放源码的、基于 Eclipse 的环境,并且可以免费下载。


  • JsUnit 是基于 JavaScript 的单元测试框架,它是通过将 JUnit(一个常见的 Java 单元测试库)移植到 JavaScript 而创建的。


  • JSLint 是一个 JavaScript 程序,用于发现 JavaScript 程序中的问题。


  • Prototype 是一个 JavaScript 库,它引入了一些强大的函数,以帮助简化 Ajax 编程。


讨论
  • 如果您想感受一下 Ajax 社区的氛围,那么请订阅 Ajaxian 博客



作者简介

Gal Shachor 是 IBM Haifa Research Lab 的一名高级技术职员和研究员,从事与中间件和 rich Internet 应用程序相关的领域。


Yoav Rubin 拥有以色列特克尼奥恩技术学院计算机科学系信息系统工程专业的学士学位。在过去 7 年的时间里,他一直在 IBM Haifa Research Lab 担任软件工程师,主要从事可视化应用程序开发工具和复杂时间处理领域。他的主要兴趣有 Java 和 Web 技术、简化的编程和可用性。


Shmulik London 是 IBM Haifa Research Lab 的研究人员。他当前的职业兴趣是软件组件模型和 Web 2.0。


Shmuel Kallner 拥有纽约 Yeshiva 大学信息科学专业的学士学位。在过去 26 年的时间里,他一直在 IBM 的不同地方担任软件工程师,



简化 XML 读写

级别: 初级

Cameron Laird (claird@phaseit.net), 副总裁, Phaseit, Inc.

2007 年 7 月 02 日

合理使用 XML,XPath 就能够显著地简化和加速应用程序。如果您的工具包中还没有 XPath,那么现在就把它添加进来吧。使用 Python 简要编写的具体示例使查询习语的出现更为自然。



XPath 理应成为您的朋友。


如果您很少使用 XML 进行开发,并且从未使用过 XPath,那么您将有机会开发性能更强、可维护性更佳的应用程序。这篇文章详细介绍了一些特定的示例,用来演示在简单的 XML 处理中,查询方法能够造成多大的影响。


基础知识


要确保 XPath 进展顺利,需考虑一些基础原理:


  • 下列示例使用 Python 编写

  • 以前您可能多次听说 Xsomething 很有用 —— 它设计用于解决特定的和实际的问题,它经过委员会的一致通过,等等。我承认我就听说过。虽然 XPath 与 XML 中所有其他的 Alphabet Soup 不同;但是它确实 非常有用的工具。对于实际投身编程工作的程序员来说,XPath 是最具学习价值的 XML 定义之一,其重要性仅次于 XML 本身和 XHTML。

  • 您不需要 XPath。您是一名实际动手编程的 XML 程序员,您的应用程序已经具备了所需的功能。可是关键在于,传统的过程编程不能很好地解决常见的 XML 问题。虽然程序能正确地运行,但是只需少量的查询代码就能使您的程序具有更好的可读性、更易于维护,并且在很多情况下,运行得更快,有时这种效果非常明显。使用这些代码的目的不是要抛弃现有的正常运作的编程方法,而是要学习一些辅助技巧,使用这些技巧可取得立杆见影的效果。



developerWork 上 XML 专区的读者大多致力于 C、C++、或 Java 开发。但 Python Python 是一种很值得展示的工具,因为它读起来像流线型的伪代码,并且广泛可用。即便是 Python 的初学者也能理解后继的用法。



类似于许多其他语言,Python 支持大量的 XML 库。过去常常会不知道从何处开始。然而,随着 Python 2.5 版本的推出,Fredrik Lundh 的非凡的 ElementTree 模块成为了标准;本文的示例将使用这个库。库本身很小,并能附加到任何 1.5.2 以上的 Python 版本中,参考资料 中对此作出了解释。正确安置好 ElementTree 后,执行示例程序所需的一切条件都已就绪。


XML 编程模型



目前大多数 XML 编程都是基于文档对象模型(Document Object Model,DOM )。这一模型将 XML 实例视为一个元素树,使用标记对元素进行标识,很多元素都有属性和/或子元素。XML 编程是一项繁重的工作,因为处理必须通过树导航到各个元素。例如,考虑 developerWorks 在开发 developerWorks 文章的内容的过程中所使用的模板 XML 。这看上去与 XHTML 极为相似。它的模式简单易懂,这当然是与工业应用程序(可能是用于采集机器或财务事项的众多细节)中常见的 XML 实例相比而言。即便是使用这个简单的模型,如果您想要报告文本中的所有锚点标记 —— 所有标记为 <a> 的元素 —— 那么您需要导航整个文档,到一个任意的嵌套深度。简单的非递归过程表达式并不能访问所有的元素,尽管许多库都包含有帮助功能,可以用于遍历树。


结果:用于报告所有锚点的一个简单的程序将类似于:


清单 1. 基于 DOM 的代码,用于报告所有的锚点




import elementtree.ElementTree

def detail_anchor(element):
if element.tag == "a":
attributes = element.attrib
if "href" in attributes.keys():
print "'%s' is at URL '%s'." % (element.text,
attributes['href'])
if "name" in attributes.keys():
print "'%s' anchors '%s'." % (element.text,
attributes['name'])

def report(element):
detail_anchor(element)
for x in element.getchildren():
report(x)

report(elementtree.ElementTree.parse("draft2.xml").getroot())




使用下面描述的 5.5.1 引用模板,将产生类似如下的输出:


清单 2. 清单 1 中的程序产生的报告




'related developerWorks content' is at 'http://www.ibm.com/developerworks'.
'entire series' is at 'http://www.ibm.com/...'
...

'IBM product evaluation versions' is at 'http://www.ibm.com/...'



如果有一种标准的方法可用来查询 DOM 实例、解压指定元素、属性和标记的信息,那么可带来多大的简化?您自己看吧:



清单 3. 基于 XPath 的与清单 1 等价的代码。



import elementtree.ElementTree

def detail_anchor(element):
if element.tag == "a":
attributes = element.attrib
if "href" in attributes.keys():
print "'%s' is at URL '%s'." % (element.text,
attributes['href'])
if "name" in attributes.keys():
print "'%s' anchors '%s'." % (element.text,
attributes['name'])

for element in \
elementtree.ElementTree.parse("draft2.xml").findall("//a"):
detail_anchor(element)


清注意,“//a” 在英语中的意思是 “在整个文档中搜索标记为 ‘a’ 的元素”。从本质上说,清单 3 产生的输出与 清单 2 的输出是相同的。



ElementTree 安装

我们希望 Python 的安装附有ElementTree。虽然大家会乐意阅读代码清单,但如果能亲自执行代码则更好,而根据自己的想法对代码进行更改并实验,这样将会甚至更佳。


这非常容易完成。与我使用过的许多笨拙的 XML 工具包相比,ElementTree 只需几分钟即可安装好并开始使用。



首先,您需要 Python。大多数当前的 UNIX 版本(包括 MacOS 和几乎所有的 Linux 变种)都内置了 Python。对于 Windows,请参阅 参考资料 中的 ActivePython。


正确安置 Python:


  1. 检索elementtree 源文件包(在 ElementTree 主页中按 参考资料 中的描述操作)。

  2. 解压该包。

  3. 导航到正确的目录(大概类似于 elementtree-1.2.6-20050316/ —— 关键是 setup.py 所在的目录)。

  4. 在命令行调用 python setup.py install



全部完成。所需操作就是这些。要检验安装,运行交互式 Python shell 并请求 import elementtree.ElementTree



比较 清单 1清单 3。前者已经够简单了 —— 可是后者更加简单。它消除了明确地指定递归的需求。更重要的是,XPath 代码根据 XML(逻辑)标记,而不是结构。标记更接近于人的思维,并且持续时间长,从这个角度来看,结构是相对短暂的实现细节。通过使用更复杂的查询,XPath 的声明性样式与传统的面向结构的过程搜索实现相比,其优势更加明显。


本文的意义在于,能够把 XPath 融入到现有的 XML 程序开发中,并能立即获得性能和可维护性上的提高。对于我来说,这更像是在使用汇编程序、编译器、和更高阶的语言:我可以完全使用机器语言编写整个程序,但是学习高生产率的方法会简单划算。此外,XPath 通常能提高 手工编码的搜索的性能。


可是,这样却产生了一个问题:有方法改进 XPath 吗?是的,当然有 —— 但是有一些限定。XQuery 和 XSLT 是另外两种接受程度与 XPath 相当的 XML 定义(XSLT 可能是它们三者中使用最为广泛的,而 Xquery 的有用实现则最少)。在声明重点上 XSLT 与 XPath 类似,而 Xquery 在 XPath 的查询中加入了过程的功能。两者都能生成模板 —— 简要地说,嵌入可识别的 XML 代码段的查询 —— 或多或少符合语言习惯。这一点值得注意,因为一些 XML 理论家建议把模板作为理想的表示形式。



但是整体来说,相对于 XPath 产生的巨大效益而言,XQuery、XSLT 或此类特定于语言的查询包(如 Amara、XQJ、或JAXP)所带来的好处都是递增的。参考资料 中提供的 Uche Ogbuji 撰写的论文清晰地比较了采用各种不同方式解决的示例问题。只有在感觉 XML 处理相当复杂的时候,才使用这些更专业的接口。但是现在就开始使用 XPath!


其它示例


这篇文章的目的是激发您使用 XPath,而不是教您使用它。我希望您可以清楚地认识到学习 XPath 并不会花费太多精力。


同时,我们希望知道使用 XPath 可实现哪些功能。清单 3 中的查询使用 //a 作为一个简单的例子。最后,您将学习更复杂的查询功能,例如 /parent/child[@attr='value'],它指定所有 “parent” 的子节点 “child” 并指定属性 “attr” 的值为 “value”。另外两个例子://@*,它检查所有标记的所有属性;//tr/td|th 检索 tr 里面的所有 tdth 元素。


性能


虽然性能测量总是需要缩小至一种特定的测量方法,但是对 XPath 性能的思考需要一些上下文。通俗地讲,XPath 比您所能达到的速度更快。在大型的、复杂的程序中,性能至关重要,XPath 查询比同等的手工编写的过程代码运行得更快(有时要快很多)。从原则上说,您可使用已有的关于数据内容或布局的知识来编写一个可快速运行的巧妙的查询。但在实践中,使用 XPath 产生的效果类似于使用高级语言(HLL)(而不是汇编程序或 C 语言)所产生的效果:只需少许的工作,开发人员就能够完成一个正确运行的程序,而 HLL 的所有表面上固有的速度缺点都可通过较高级的特定于应用程序的算法来弥补。


使用完全相同的方式,XPath 对一些不可忽视问题简化了编码。在这些问题中,速度造成的影响足以使程序员利用空闲时间小心地开发自己的解决方案。基于 XPath 的解决方案不仅更易于纠错和维护,而且至少是速度快。


此外,一些 XPath 实现是非常细致地编写的,其内存使用率比普通的过程搜索更高。在这些例子中,基于 XPath 的编码能极大地提高搜索速度 —— 十倍或更高。


没有展示特定的测量方法来说明这些问题确实令人沮丧。虽然,任何特定的对比都会产生误导;但是 XPath 的相对性能取决于开发语言、特定的 XPath 实现、XML 查询图像、搜索、和运行时可用内存(大多数情况)的布局。学习如何进行简单的计时,并学习在您的 应用程序的数据集上测试 XPath。如果您的经验和我差不多,那么您时常会发现 XPath 只有原生编码一半的速度,对于一些小问题尤为显著,但是与其它语言相比速度还是要快一个级别。


结束语




分享这篇文章……







digg



将本文提交到 Digg




del.icio.us


发布到 del.icio.us





Slashdot




提交到 Slashdot!






XPath 是一种 XML 工具,使用成本极低:您可能已通过使用的开发语言将其构建到 XML 库中。同时,XPath 具有显著提高性能的潜力,并且其简单的表示实实在在地简化了编程和维护。此外,还有大量的 XPath 教程能帮助您学习所需知识。最终,您将发现在 XPath 上的投入能很快带来收益。


一旦了解了 XPath,您也将能更好地判断是否使用 XML 时太费劲,需要利用一些高级工具(如XQuery 和 XSLT)来使您受益。


务必阅读参考资料中列出的资料,它们能帮助您立即开始使用 XPath 编程。




参考资料

学习
  • 您可以参阅本文在 developerWorks 全球网站上的 英文原文

  • developerWork 的 “可爱的 Python” 专栏:为什么要使用 Python 来解释 XPath?有关这一语言优点的细节,请参见 David Mertz 的专栏。近来,它得到了广泛的应用,而且就算是完全不了解它的程序员往往也会发现它易于阅读。


  • ActivePython:探索 ActivePython,依据 ActivePython 的主页,ActivePython 是 Python 的一种有质量保证的可安装的发行版。因为其安装的可靠性,作者特别建议对 Python 感兴趣的 Windows 用户使用 ActivePython。


  • ElementTree 主页:查看由 Fredrik Lundh 编写和维护的 ElementTree 包装器如何添加代码,以便将 XML 文件作为 Element 对象的树进行加载,并将它们保存回来。除了 上面侧栏 中讨论的安装简单之外,ElementTree 还附带了各种实现以利用其它的库和提高性能。

  • 大量 关于 XPath 的教程中,可以从这里开始学习:


    • XPath 入门(Bertrand Portier,developerWorks,2004 年 5 月):学习什么是 XPath,XPath 语言的语法和语义,如何使用 XPath 位置路径,如何使用 XPath 表达式,如何使用 XPath 函数,以及 XPath 如何与 XSLT 关联。本教程介绍的是 XPath 1.0 版。


    • XPath tutorial(Miloslav Nic 和 Jiri Jirat,zvon):通过大量的例子学习所选的 XPath 特性。




  • XML Path Language (XPath)
    规范
    :有关 XSL Transformations [XSLT] 和 Xpointer 共享功能的常用语法和语义,请阅读 W3C 维护规范。


  • 实用数据绑定: 使用 XPath 作为数据绑定工具,第 2 部分(Brett McLaughlin,developerWorks,2006 年 1 月):使用 JAXP API 执行 XPath 查询并在 Java 中使用 XPath 进行编码。

  • XGrep 是一种类似于 grep 的 XML 文档工具。” Xgrep 是一款非常出色的工具,开发人员应该时刻把它放在身边。这篇文章使用 Python 作为示例演示了 XML 编程。但是,如果 Cameron 的介绍重点是 XPath 本身,而不是如何使用它编程,则他就会根据 XGrep 进行讲授。

  • 阅读下列两篇 developerWork 的介绍性文章:


    • XQuery 简介(Howard Katz,2006 年 2 月更新):阅读 W3C 提议的 XML 查询语言标准。


    • XSLT 是什么类型的语言?(Michael Kay,2001 年 2 月):把 XSLT 放置在上下文中 —— 它的起源、擅长方面以及为什么要使用它。



  • 看看两位作者的不同见解,但是两篇文章都是经过仔细的斟酌而写成的;它们可很好地相互补充。



  • developerWork 中国网站 XML 专区:了解 XML 的所有信息。


  • IBM XML 认证:了解如何才能成为一名 IBM 认证的 XML 及相关技术的开发人员。


  • XML 技术文档库: developerWorks XML 专区提供了大量技术文章和提示、教程、标准以及 IBM 红皮书。


  • developerWorks 技术活动和网络广播:随时关注技术的最新进展。


获得产品和技术

  • IBM 试用版软件:使用 IBM 试用软件构建您的下一个开发项目,可从 developerWorks 直接下载获得。


讨论


关于作者

Photo of Cameron Laird

Cameron Laird 是 developerWorks 长期投稿者和前专栏作家。他经常编写关于促进其公司应用程序开发的项目的文章,主要关注可靠性、安全性和最初不是为协作而设计的系统集成。

J2EE程序中的SQL语句自动构造方法讲解 — IT技术 - 赛迪网

INSERT、DELETE、UPDATE 三种SQL语句是数据库技术的三大基本语句。 在通常的web开发中对它的处理可以说是无处不在. 如果简单的都用手工来构造这些SQL语句的话, 一方面给我们的开发带来很大的工作量, 另一方面系统灵活性受到很大的限制。那么能不能基于某种规则让系统自动从页面表单中取出元素构造出SQL语句呢? 首先让我们看看一般INSERT、DELETE、UPDATE 三种语句的基本形式:







INSERT INTO table_name (col_1,col_2,col_3,) VALUES (value_1,value_2,value_3 …) 

DELETE FROM table_name WHERE col_n=value_n

UPDATE table_name SET col_1=value_1,col_2=value_2,col_3=value_3 WHERE col_x=value_x



我们知道,借用j2ee中的request.getParameterNames()方法可以读到表单中的所有元素的名称,有了元素名称借用request.getParameter(elementName)方法可以获取该元素的值。假设在开发中我们让页面元素的名称和底层数据库表的字段名一致。那么在这三种语句中col_n 和 value_n 对我们来说就不是未知的,未知的数据就剩下了 table_name,col_x和value_x 。现在如果我们写一个方法,传入request对象,再把table_name,col_x,value_x作为参数传入方法,那么我们可以轻松的自动构造SQL语句了。

但这样做还是有欠灵活,因为一方面每一次使用该方法我们都得人工的设置table_name,col_x和value_x;另一方面别忘了sql语句中对于字符串的字段需要加单引号和替换字符串中间的单引号,而整型、浮点型、系统函数(如now(),to_date()等数据库函数)等不需要做单引号的处理,这些如果没有好的解决的话,我们的方法将受到非常大的限制。要达到再进一步分离最好的办法就是在表单元素命名上面做文章,我们可以自己定义一套元素命名规则,对不同规则命名的元素做不同的处理--设我们定义元素命名规格如下:

1. table_name,col_x,value_x这类元素,为公共元素。我们规定这类元素名以c_k开头(c=common),我们限制table_name的元素名为c_table,col_x=value_x定义到一起,元素名定为c_where. 当然我们别忘了我们还需要一个元素表示构造什么样(INSERT、DELETE、UPDATE)的SQL语句。我们给这个元素命名c_genre,它的值被限制在INSERT、DELETE、UPDATE这三者之中 。

2. 对于表单中对应数据库字符串类型的元素,在SQL构造中需要做单引号的处理。这类元素我们暂且称他们为字符串型元素。字符串型元素我们规定其名为s_+数据库表字段名 (s=String)。

3. 对于不需要做但引号处理的元素(如integer型、float型、数据库系统函数--如now(),to_date()等等)。我们暂且简单的统称这类元素为整型元素。对于整型元素我们限制其命名规则为i_+数据库表字段名(i=Integer)。

基于上面的规格我们可以非常轻松写一个javabean。代码如下:







/**
* @version: 1.1
* @Time: 2005.03.02
*/
package com.river.page ;
import java.util.*;
import javax.servlet.http.HttpServletRequest;
public class PageUtil {
  private HttpServletRequest request = null ;
  public PageUtil(){}
  public void init(HttpServletRequest _request){
   this.request = _request ;
  }
  public void clear(){
   if(this.request != null){
   this.request = null ;
  }
}
public String get(String elementName){
  if(request == null || request.getParameter(elementName) == null){
   return "";
  }else{
   return request.getParameter(elementName);
  }
}

public String get(HttpServletRequest _request,String elementName){
  init(_request);
  return get(elementName);
}

public String getSQL(HttpServletRequest _request){
  init(_request);
  return getSQL();
}
public String getSQL(){
  String sqlstr = "";
  String c_table = get("c_table");
  String c_genre = get("c_genre");
  String c_where = get("c_where");
  if(c_genre == null || c_genre.equals("")){
   return "the action is null/empty";
  }
  if(c_table == null || c_table.equals("")){
   return "unknow table/empty" ;
  }
  if(c_genre.equalsIgnoreCase("INSERT")){
   java.util.Enumeration arg_names = request.getParameterNames();
   String colstr = "",valstr = "";
   String arg_name,pre_name,end_name ;
   while(arg_names.hasMoreElements()){
    arg_name = String.valueOf(arg_names.nextElement());
    if(arg_name.length() < 2){
     continue;
    }
    pre_name = arg_name.substring(0,2);
    end_name = arg_name.substring(2);
    if(pre_name.equalsIgnoreCase("i_")){
     colstr = colstr+","+end_name;
     if(get(arg_name).equals("")){
      valstr = valstr+",NULL";
     }else{
      valstr = valstr + "," + String.valueOf(get(arg_name));
     }
    }else if(pre_name.equalsIgnoreCase("s_")){
     colstr = colstr+","+end_name;
     if(get(arg_name).equals("")){
      valstr = valstr+",NULL";
     }else{
      valstr = valstr+",'"+get(arg_name).replaceAll("'","''")+"'";
    }
   }
  }
  if(!colstr.equals("")){
   colstr = colstr.substring(1);
   valstr = valstr.substring(1);
  }
  sqlstr = "INSERT INTO "+c_table+" ("+colstr+") VALUES ("+valstr+")";
  return sqlstr;
}else if(c_genre.equalsIgnoreCase("UPDATE")){
  java.util.Enumeration arg_names = request.getParameterNames();
  String colstr = "";
  String arg_name,pre_name,end_name ;
  while(arg_names.hasMoreElements()){
   arg_name = String.valueOf(arg_names.nextElement()).trim();
   if(arg_name.length() < 2){
    continue;
   }
   pre_name = arg_name.substring(0,2);
   end_name = arg_name.substring(2);
   if(pre_name.equalsIgnoreCase("i_")){
    if(get(arg_name).equals("")){
     colstr += ","+end_name+"=NULL";
    }else{
     colstr += ","+end_name+"="+get(arg_name);
   }
  }else if(pre_name.equalsIgnoreCase("s_")){
   if(get(arg_name).equals("")){
    colstr += ","+end_name+"="+get(arg_name);
   }else{
    colstr += ","+end_name+"='"+get(arg_name).replaceAll("'","''")+"'";
   }
  }
}
if(!colstr.equals("")){
  colstr = colstr.substring(1);
}
sqlstr = "UPDATE "+c_table+" SET "+colstr;
if(!c_where.equals("")){
  sqlstr += " WHERE "+c_where;
}
return sqlstr;
}else if(c_genre.equalsIgnoreCase("DELETE")){
sqlstr = "DELETE FROM "+c_table;
if(c_where != null && !c_where.equals("")){
  sqlstr += " WHERE "+c_where;
}
}else{
  com.river.debug.Debug.show("unknow action type : "+c_genre);
  return null;
}
return sqlstr;
}
public String toString(){
  return "version 1.0, date 2005.03.02, author river";
}
}



这样我们就可以根据页面元素的命名来指导SQL语句的生成。这样做有很多的明显的好处:

1. 减少编码工作,对于元素很多表单,用不着我们去写一大堆的代码,不用去担心哪个元素落下了,元素名有没有些错,单引号有没有处理。

2. 通用、稳定、易于维护,javabean固有的优点,就不用太多的说明了。

3. 分离表层的表单内容与逻辑层SQL语句的构造。设想一下,如果我们数据库表结构有调整时,那么我们只要修改一下表单就好了,根本就不用理原来写好的逻辑处理。附带着再说一句,设想如果我们再写一个类自动执行SQL,那么对于一些基本的增、删、改操作都可以映射到同一个action里面来处理,且不是很爽?

当然,这样做的缺点也是有的。那就是有一定的性能损耗。特别是碰到表单元素非常多时。但是对于那些不是很"苛刻"的项目这点损耗是值得的。(责任编辑:龚勋)







Monday, July 30, 2007

JavaScript 表格排序 -《JavaScript高级程序设计》源码

《JavaScript高级程序设计》,英文书名为《Professional JavaScript for Web Developers》中的源码。

书是本绝对的好书,是《JavaScript权威指南》很好的补充。

第十二章,表格排序的原码,支持字符串,整形,浮点型,日期型,其它如图标排序。

原码直接下载地址:http://media.wiley.com/product_ancillary/88/07645790/DOWNLOAD/579088CodeExamples.zip

解压后,在文件夹 Chapter 12 里。

一、字符串,整形,浮点型,日期型排序

<html>
<head>
<title>Table Sort Example</title>
<script type="text/javascript">

function convert(sValue, sDataType) {
switch(sDataType) {
case "int":
return parseInt(sValue);
case "float":
return parseFloat(sValue);
case "date":
return new Date(Date.parse(sValue));
default:
return sValue.toString();

}
}

function generateCompareTRs(iCol, sDataType) {

return function compareTRs(oTR1, oTR2) {
var vValue1 = convert(oTR1.cells[iCol].firstChild.nodeValue, sDataType);
var vValue2 = convert(oTR2.cells[iCol].firstChild.nodeValue, sDataType);

if (vValue1 < vValue2) {
return -1;
}
else if (vValue1 > vValue2) {
return 1;
}
else {
return 0;
}
};
}

function sortTable(sTableID, iCol, sDataType) {
var oTable = document.getElementById(sTableID);
var oTBody = oTable.tBodies[0];
var colDataRows = oTBody.rows;
var aTRs = new Array;

for (var i=0; i < colDataRows.length; i++) {
aTRs[i]
= colDataRows[i];
}

if (oTable.sortCol == iCol) {
aTRs.reverse();
}
else {
aTRs.sort(generateCompareTRs(iCol, sDataType));
}

var oFragment = document.createDocumentFragment();
for (var i=0; i < aTRs.length; i++) {
oFragment.appendChild(aTRs[i]);
}

oTBody.appendChild(oFragment);
oTable.sortCol
= iCol;
}

</script>
</head>
<body>
<p>Click on the table header to sort in ascending order.</p>
<table border="1" id="tblSort">
<thead>
<tr>
<th onclick="sortTable('tblSort', 0)"
style
="cursor:pointer">Last Name</th>
<th onclick="sortTable('tblSort', 1)"
style
="cursor:pointer">First Name</th>
<th onclick="sortTable('tblSort', 2, 'date')"
style
="cursor:pointer">Birthday</th>
<th onclick="sortTable('tblSort', 3, 'int')"
style
="cursor:pointer">Siblings</th>
</tr>
</thead>
<tbody>
<tr>
<td>Smith</td>
<td>John</td>
<td>7/12/1978</td>
<td>2</td>
</tr>
<tr>
<td>Johnson</td>
<td>Betty</td>
<td>10/15/1977</td>
<td>4</td>
</tr>
<tr>
<td>Henderson</td>
<td>Nathan</td>
<td>2/25/1949</td>
<td>1</td>
</tr>
<tr>
<td>Williams</td>
<td>James</td>
<td>7/8/1980</td>
<td>4</td>
</tr>
<tr>
<td>Gilliam</td>
<td>Michael</td>
<td>7/22/1949</td>
<td>1</td>
</tr>
<tr>
<td>Walker</td>
<td>Matthew</td>
<td>1/14/2000</td>
<td>3</td>
</tr>
</tbody>
</table>
</body>
</html>

二、带图标的排序

<html>
<head>
<title>Table Sort Example</title>
<script type="text/javascript">

function convert(sValue, sDataType) {
switch(sDataType) {
case "int":
return parseInt(sValue);
case "float":
return parseFloat(sValue);
case "date":
return new Date(Date.parse(sValue));
default:
return sValue.toString();

}
}

function generateCompareTRs(iCol, sDataType) {

return function compareTRs(oTR1, oTR2) {
var vValue1, vValue2;

if (oTR1.cells[iCol].getAttribute("value")) {
vValue1
= convert(oTR1.cells[iCol].getAttribute("value"),
sDataType);
vValue2
= convert(oTR2.cells[iCol].getAttribute("value"),
sDataType);
}
else {
vValue1
= convert(oTR1.cells[iCol].firstChild.nodeValue,
sDataType);
vValue2
= convert(oTR2.cells[iCol].firstChild.nodeValue,
sDataType);
}

if (vValue1 < vValue2) {
return -1;
}
else if (vValue1 > vValue2) {
return 1;
}
else {
return 0;
}
};
}

function sortTable(sTableID, iCol, sDataType) {
var oTable = document.getElementById(sTableID);
var oTBody = oTable.tBodies[0];
var colDataRows = oTBody.rows;
var aTRs = new Array;

for (var i=0; i < colDataRows.length; i++) {
aTRs[i]
= colDataRows[i];
}

if (oTable.sortCol == iCol) {
aTRs.reverse();
}
else {
aTRs.sort(generateCompareTRs(iCol, sDataType));
}

var oFragment = document.createDocumentFragment();
for (var i=0; i < aTRs.length; i++) {
oFragment.appendChild(aTRs[i]);
}

oTBody.appendChild(oFragment);
oTable.sortCol
= iCol;
}

</script>
</head>
<body>
<p>Click on the table header to sort.</p>
<table border="1" id="tblSort">
<thead>
<tr>
<th onclick="sortTable('tblSort', 0)" style="cursor:pointer">Type</th>
<th onclick="sortTable('tblSort', 1)" style="cursor:pointer">Filename</th>
</tr>
</thead>
<tbody>
<tr>
<td value="doc"><img src="images/wordicon.gif"/></td>
<td>My Resume.doc</td>
</tr>
<tr>
<td value="xls"><img src="images/excelicon.gif"/></td>
<td>Fall Budget.xls</td>
</tr>
<tr>
<td value="pdf"><img src="images/acrobaticon.gif"/></td>
<td>How to be a better programmer.pdf</td>
</tr>
<tr>
<td value="doc"><img src="images/wordicon.gif"/></td>
<td>My Old Resume.doc</td>
</tr>
<tr>
<td value="txt"><img src="images/notepadicon.gif"/></td>
<td>Notes from Meeting.txt</td>
</tr>
<tr>
<td value="zip"><img src="images/zippedfoldericon.gif"/></td>
<td>Backups.zip</td>
</tr>
<tr>
<td value="xls"><img src="images/excelicon.gif"/></td>
<td>Spring Budget.xls</td>
</tr>
<tr>
<td value="doc"><img src="images/wordicon.gif"/></td>
<td>Job Description - Web Designer.doc</td>
</tr>
<tr>
<td value="pdf"><img src="images/acrobaticon.gif"/></td>
<td>Saved Web Page.pdf</td>
</tr>
<tr>
<td value="doc"><img src="images/wordicon.gif"/></td>
<td>Chapter 1.doc</td>
</tr>
</tbody>
</table>
</body>
</html>

从追MM谈Java的23种设计模式 - 太经典了,转到自己的BLOG上收藏着先

从追MM谈Java的23种设计模式 - 太经典了,转到自己的BLOG上收藏着先

设计模式做为程序员的“内功心法”,越来越受到.net 社区的重视,这种变化是很可喜的,Java社区走在了我们的前面,但这种状况也许有一天会发生改变。

从追MM谈Java的23种设计模式
1、FACTORY—追MM少不了请吃饭了,麦当劳的鸡翅和肯德基的鸡翅都是MM爱吃的东西,虽然口味有所不同,但不管你带MM去麦当劳或肯德基,只管向服务员说“来四个鸡翅”就行了。麦当劳和肯德基就是生产鸡翅的Factory.

  工厂模式:客户类和工厂类分开。消费者任何时候需要某种产品,只需向工厂请求即可。消费者无须修改就可以接纳新产品。缺点是当产品修改时,工厂类也要做相应的修改。如:如何创建及如何向客户端提供。
  

程序代码
  public class Factory{
    public String Boy = "boy" ;
    public String Girl = "girl" ;
    public People getPeople(String people){
      if (people.equals("boy")){
        return new Boy();
      }else if(people.equals("girl")){
        return new Girl();
      }
    }
  }

  2、BUILDER—MM最爱听的就是“我爱你”这句话了,见到不同地方的MM,要能够用她们的方言跟她说这句话哦,我有一个多种语言翻译机,上面每种语言都有一个按键,见到MM我只要按对应的键,它就能够用相应的语言说出“我爱你”这句话了,国外的MM也可以轻松搞掂,这就是我的“我爱你”builder。(这一定比美军在伊拉克用的翻译机好卖)

  建造模式:将产品的内部表象和产品的生成过程分割开来,从而使一个建造过程生成具有不同的内部表象的产品对象。建造模式使得产品内部表象可以独立的变化,客户不必知道产品内部组成的细节。建造模式可以强制实行一种分步骤进行的建造过程。

  3、FACTORY METHOD—请MM去麦当劳吃汉堡,不同的MM有不同的口味,要每个都记住是一件烦人的事情,我一般采用Factory Method模式,带着MM到服务员那儿,说“要一个汉堡”,具体要什么样的汉堡呢,让MM直接跟服务员说就行了。

  工厂方法模式:核心工厂类不再负责所有产品的创建,而是将具体创建的工作交给子类去做,成为一个抽象工厂角色,仅负责给出具体工厂类必须实现的接口,而不接触哪一个产品类应当被实例化这种细节。

  4、PROTOTYPE—跟MM用QQ聊天,一定要说些深情的话语了,我搜集了好多肉麻的情话,需要时只要copy出来放到QQ里面就行了,这就是我的情话prototype了。(100块钱一份,你要不要)

  原始模型模式:通过给出一个原型对象来指明所要创建的对象的类型,然后用复制这个原型对象的方法创建出更多同类型的对象。原始模型模式允许动态的增加或减少产品类,产品类不需要非得有任何事先确定的等级结构,原始模型模式适用于任何的等级结构。缺点是每一个类都必须配备一个克隆方法。

  5、SINGLETON—俺有6个漂亮的老婆,她们的老公都是我,我就是我们家里的老公Sigleton,她们只要说道“老公”,都是指的同一个人,那就是我(刚才做了个梦啦,哪有这么好的事)

  单例模式:单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例单例模式。单例模式只应在有真正的“单一实例”的需求时才可使用。

  public class SingLeton{
    private static SingLeton instance = new SingLeton();
    public static SingLeton getInstance(){
      return instance;
    }
  } 

  6、ADAPTER—在朋友聚会上碰到了一个美女Sarah,从香港来的,可我不会说粤语,她不会说普通话,只好求助于我的朋友kent了,他作为我和Sarah之间的Adapter,让我和Sarah可以相互交谈了(也不知道他会不会耍我)

  适配器(变压器)模式:把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口原因不匹配而无法一起工作的两个类能够一起工作。适配类可以根据参数返还一个合适的实例给客户端。

  7、BRIDGE—早上碰到MM,要说早上好,晚上碰到MM,要说晚上好;碰到MM穿了件新衣服,要说你的衣服好漂亮哦,碰到MM新做的发型,要说你的头发好漂亮哦。不要问我“早上碰到MM新做了个发型怎么说”这种问题,自己用BRIDGE组合一下不就行了

  桥梁模式:将抽象化与实现化脱耦,使得二者可以独立的变化,也就是说将他们之间的强关联变成弱关联,也就是指在一个软件系统的抽象化和实现化之间使用组合/聚合关系而不是继承关系,从而使两者可以独立的变化。

  8、COMPOSITE—Mary今天过生日。“我过生日,你要送我一件礼物。”“嗯,好吧,去商店,你自己挑。”“这件T恤挺漂亮,买,这条裙子好看,买,这个包也不错,买。”“喂,买了三件了呀,我只答应送一件礼物的哦。”“什么呀,T恤加裙子加包包,正好配成一套呀,小姐,麻烦你包起来。”“……”,MM都会用Composite模式了,你会了没有?

  合成模式:合成模式将对象组织到树结构中,可以用来描述整体与部分的关系。合成模式就是一个处理对象的树结构的模式。合成模式把部分与整体的关系用树结构表示出来。合成模式使得客户端把一个个单独的成分对象和由他们复合而成的合成对象同等看待。

  9、DECORATOR—Mary过完轮到Sarly过生日,还是不要叫她自己挑了,不然这个月伙食费肯定玩完,拿出我去年在华山顶上照的照片,在背面写上“最好的的礼物,就是爱你的Fita”,再到街上礼品店买了个像框(卖礼品的MM也很漂亮哦),再找隔壁搞美术设计的Mike设计了一个漂亮的盒子装起来……,我们都是Decorator,最终都在修饰我这个人呀,怎么样,看懂了吗?

  装饰模式:装饰模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案,提供比继承更多的灵活性。动态给一个对象增加功能,这些功能可以再动态的撤消。增加由一些基本功能的排列组合而产生的非常大量的功能。

  10、FAÇADE—我有一个专业的Nikon相机,我就喜欢自己手动调光圈、快门,这样照出来的照片才专业,但MM可不懂这些,教了半天也不会。幸好相机有Façade设计模式,把相机调整到自动档,只要对准目标按快门就行了,一切由相机自动调整,这样MM也可以用这个相机给我拍张照片了。

  门面模式:外部与一个子系统的通信必须通过一个统一的门面对象进行。门面模式提供一个高层次的接口,使得子系统更易于使用。每一个子系统只有一个门面类,而且此门面类只有一个实例,也就是说它是一个单例模式。但整个系统可以有多个门面类。

  11、FLYWEIGHT—每天跟MM发短信,手指都累死了,最近买了个新手机,可以把一些常用的句子存在手机里,要用的时候,直接拿出来,在前面加上MM的名字就可以发送了,再不用一个字一个字敲了。共享的句子就是Flyweight,MM的名字就是提取出来的外部特征,根据上下文情况使用。

  享元模式:FLYWEIGHT在拳击比赛中指最轻量级。享元模式以共享的方式高效的支持大量的细粒度对象。享元模式能做到共享的关键是区分内蕴状态和外蕴状态。内蕴状态存储在享元内部,不会随环境的改变而有所不同。外蕴状态是随环境的改变而改变的。外蕴状态不能影响内蕴状态,它们是相互独立的。将可以共享的状态和不可以共享的状态从常规类中区分开来,将不可以共享的状态从类里剔除出去。客户端不可以直接创建被共享的对象,而应当使用一个工厂对象负责创建被共享的对象。享元模式大幅度的降低内存中对象的数量。

  12、PROXY—跟MM在网上聊天,一开头总是“hi,你好”,“你从哪儿来呀?”“你多大了?”“身高多少呀?”这些话,真烦人,写个程序做为我的Proxy吧,凡是接收到这些话都设置好了自动的回答,接收到其他的话时再通知我回答,怎么样,酷吧。

  代理模式:代理模式给某一个对象提供一个代理对象,并由代理对象控制对源对象的引用。代理就是一个人或一个机构代表另一个人或者一个机构采取行动。某些情况下,客户不想或者不能够直接引用一个对象,代理对象可以在客户和目标对象直接起到中介的作用。客户端分辨不出代理主题对象与真实主题对象。代理模式可以并不知道真正的被代理对象,而仅仅持有一个被代理对象的接口,这时候代理对象不能够创建被代理对象,被代理对象必须有系统的其他角色代为创建并传入。
  
  public interface FactoryProxy{
    public People createBoy();
    public People creteGirl();
  }

  13、CHAIN OF RESPONSIBLEITY—晚上去上英语课,为了好开溜坐到了最后一排,哇,前面坐了好几个漂亮的MM哎,找张纸条,写上“Hi,可以做我的女朋友吗?如果不愿意请向前传”,纸条就一个接一个的传上去了,糟糕,传到第一排的MM把纸条传给老师了,听说是个老处女呀,快跑!

  责任链模式:在责任链模式中,很多对象由每一个对象对其下家的引用而接

  起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。客户并不知道链上的哪一个对象最终处理这个请求,系统可以在不影响客户端的情况下动态的重新组织链和分配责任。处理者有两个选择:承担责任或者把责任推给下家。一个请求可以最终不被任何接收端对象所接受。

  14、COMMAND—俺有一个MM家里管得特别严,没法见面,只好借助于她弟弟在我们俩之间传送信息,她对我有什么指示,就写一张纸条让她弟弟带给我。这不,她弟弟又传送过来一个COMMAND,为了感谢他,我请他吃了碗杂酱面,哪知道他说:“我同时给我姐姐三个男朋友送COMMAND,就数你最小气,才请我吃面。”,

  命令模式:命令模式把一个请求或者操作封装到一个对象中。命令模式把发出命令的责任和执行命令的责任分割开,委派给不同的对象。命令模式允许请求的一方和发送的一方独立开来,使得请求的一方不必知道接收请求的一方的接口,更不必知道请求是怎么被接收,以及操作是否执行,何时被执行以及是怎么被执行的。系统支持命令的撤消。

  15、INTERPRETER—俺有一个《泡MM真经》,上面有各种泡MM的攻略,比如说去吃西餐的步骤、去看电影的方法等等,跟MM约会时,只要做一个Interpreter,照着上面的脚本执行就可以了。

  解释器模式:给定一个语言后,解释器模式可以定义出其文法的一种表示,并同时提供一个解释器。客户端可以使用这个解释器来解释这个语言中的句子。解释器模式将描述怎样在有了一个简单的文法后,使用模式设计解释这些语句。在解释器模式里面提到的语言是指任何解释器对象能够解释的任何组合。在解释器模式中需要定义一个代表文法的命令类的等级结构,也就是一系列的组合规则。每一个命令对象都有一个解释方法,代表对命令对象的解释。命令对象的等级结构中的对象的任何排列组合都是一个语言。

  16、ITERATOR—我爱上了Mary,不顾一切的向她求婚。

  Mary:“想要我跟你结婚,得答应我的条件”

  我:“什么条件我都答应,你说吧”

  Mary:“我看上了那个一克拉的钻石”

  我:“我买,我买,还有吗?”

  Mary:“我看上了湖边的那栋别墅”

  我:“我买,我买,还有吗?”

  Mary:“我看上那辆法拉利跑车”

  我脑袋嗡的一声,坐在椅子上,一咬牙:“我买,我买,还有吗?”

  ……

  迭代子模式:迭代子模式可以顺序访问一个聚集中的元素而不必暴露聚集的内部表象。多个对象聚在一起形成的总体称之为聚集,聚集对象是能够包容一组对象的容器对象。迭代子模式将迭代逻辑封装到一个独立的子对象中,从而与聚集本身隔开。迭代子模式简化了聚集的界面。每一个聚集对象都可以有一个或一个以上的迭代子对象,每一个迭代子的迭代状态可以是彼此独立的。迭代算法可以独立于聚集角色变化。

  17、MEDIATOR—四个MM打麻将,相互之间谁应该给谁多少钱算不清楚了,幸亏当时我在旁边,按照各自的筹码数算钱,赚了钱的从我这里拿,赔了钱的也付给我,一切就OK啦,俺得到了四个MM的电话。

  调停者模式:调停者模式包装了一系列对象相互作用的方式,使得这些对象不必相互明显作用。从而使他们可以松散偶合。当某些对象之间的作用发生改变时,不会立即影响其他的一些对象之间的作用。保证这些作用可以彼此独立的变化。调停者模式将多对多的相互作用转化为一对多的相互作用。调停者模式将对象的行为和协作抽象化,把对象在小尺度的行为上与其他对象的相互作用分开处理。

  18、MEMENTO—同时跟几个MM聊天时,一定要记清楚刚才跟MM说了些什么话,不然MM发现了会不高兴的哦,幸亏我有个备忘录,刚才与哪个MM说了什么话我都拷贝一份放到备忘录里面保存,这样可以随时察看以前的记录啦。

  备忘录模式:备忘录对象是一个用来存储另外一个对象内部状态的快照的对象。备忘录模式的用意是在不破坏封装的条件下,将一个对象的状态捉住,并外部化,存储起来,从而可以在将来合适的时候把这个对象还原到存储起来的状态。

  19、OBSERVER—想知道咱们公司最新MM情报吗?加入公司的MM情报邮件组就行了,tom负责搜集情报,他发现的新情报不用一个一个通知我们,直接发布给邮件组,我们作为订阅者(观察者)就可以及时收到情报啦

  观察者模式:观察者模式定义了一种一队多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使他们能够自动更新自己。

  20、STATE—跟MM交往时,一定要注意她的状态哦,在不同的状态时她的行为会有不同,比如你约她今天晚上去看电影,对你没兴趣的MM就会说“有事情啦”,对你不讨厌但还没喜欢上的MM就会说“好啊,不过可以带上我同事么?”,已经喜欢上你的MM就会说“几点钟?看完电影再去泡吧怎么样?”,当然你看电影过程中表现良好的话,也可以把MM的状态从不讨厌不喜欢变成喜欢哦。

  状态模式:状态模式允许一个对象在其内部状态改变的时候改变行为。这个对象看上去象是改变了它的类一样。状态模式把所研究的对象的行为包装在不同的状态对象里,每一个状态对象都属于一个抽象状态类的一个子类。状态模式的意图是让一个对象在其内部状态改变的时候,其行为也随之改变。状态模式需要对每一个系统可能取得的状态创立一个状态类的子类。当系统的状态变化时,系统便改变所选的子类。

  21、STRATEGY—跟不同类型的MM约会,要用不同的策略,有的请电影比较好,有的则去吃小吃效果不错,有的去海边浪漫最合适,单目的都是为了得到MM的芳心,我的追MM锦囊中有好多Strategy哦。

  策略模式:策略模式针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。策略模式把行为和环境分开。环境类负责维持和查询行为类,各种算法在具体的策略类中提供。由于算法和环境独立开来,算法的增减,修改都不会影响到环境和客户端。

  22、TEMPLATE METHOD——看过《如何说服女生上床》这部经典文章吗?女生从认识到上床的不变的步骤分为巧遇、打破僵局、展开追求、接吻、前戏、动手、爱抚、进去八大步骤(Template method),但每个步骤针对不同的情况,都有不一样的做法,这就要看你随机应变啦(具体实现);

  模板方法模式:模板方法模式准备一个抽象类,将部分逻辑以具体方法以及具体构造子的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现。先制定一个顶级逻辑框架,而将逻辑的细节留给具体的子类去实现。

  23、VISITOR—情人节到了,要给每个MM送一束鲜花和一张卡片,可是每个MM送的花都要针对她个人的特点,每张卡片也要根据个人的特点来挑,我一个人哪搞得清楚,还是找花店老板和礼品店老板做一下Visitor,让花店老板根据MM的特点选一束花,让礼品店老板也根据每个人特点选一张卡,这样就轻松多了;

  访问者模式:访问者模式的目的是封装一些施加于某种数据结构元素之上的操作。一旦这些操作需要修改的话,接受这个操作的数据结构可以保持不变。访问者模式适用于数据结构相对未定的系统,它把数据结构和作用于结构上的操作之间的耦合解脱开,使得操作集合可以相对自由的演化。访问者模式使得增加新的操作变的很容易,就是增加一个新的访问者类。访问者模式将有关的行为集中到一个访问者对象中,而不是分散到一个个的节点类中。当使用访问者模式时,要将尽可能多的对象浏览逻辑放在访问者类中,而不是放到它的子类中。访问者模式可以跨过几个类的等级结构访问属于不同的等级结构的成员类。



Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1707648