内容


JSP 标记库:着意设计的更好的可用性

声明标记改善了 JSP 页面的可用性

Comments

将底层内容模型与表示分离是件好事,这在 Web 应用开发人员中间得到了普遍的认同。在多数大型开发工程中,程序员负责实现后端,表示则留给一名或多名 Web 页面设计人员去做。这种分工确保了最终产品技术可靠、同时表示良好,但它肯定要求两个小组有效地沟通和合作。考虑到每个小组工作时所依赖的知识基础不同,而且关注的问题也极其不同,这可能是一个挑战。

在引入 JavaServer Pages(JSP)规范的版本 1.1 中的定制标记库之前,必须使用 JSP 脚本元素来在 JSP 页面内提供任意的定制功能性。显式地使用脚本元素违背了模型与表示相分离的原则。显式地使用脚本元素还要求,如果 Web 页面设计人员要做任何“从 JavaBean 组件中检索属性”之外的事情,他就要具有 Java 编程经验。这引起了人们对 JSP 页面内脚本元素的使用的广泛关注,这接着导致了人们进行替代解决方案的开发。

一个经典的解决方案是开发出了模型-视图-控制器(Model-View-Controller)类型的使用模型。在这种使用模型中,应用程序的智能部分放置在 servlet 和 bean 中,JSP 页面只负责检索内容并将它呈现出来。Jakarta Struts 就是这种模型的一个很好的示例。别的开发小组已经创建了诸如 Velocity 内容引擎或 Apache 的 Cocoon 工程这样的替代技术。

虽然这些解决方案各有千秋,但它们通常是非标准的,而且忽视了 JSP 技术的持续发展。我们将在本文着重讨论 JSP 技术中使用最不充分的实用技术之一:定制标记库。我的目标是改变您对定制标记库(更具体地说,是标记设计)的思考方式。我的讨论将从澄清关于 JSP 技术及其某些替代解决方案的一些误解开始,然后再转到中心论题上。

JSP 技术:误解和替代解决方案

关于 JSP 技术的常见误解有很多,其中一些误解通常已经过时,而且还吓走了潜在的用户。表 1 列出了一些最常见的误解。

表 1. JSP:误解
误解真相
JSP 页面要求以脚本元素的形式对代码模型和表示进行混合。正如我们将在下一部分讨论的那样,标记库证明了这种说法是错误的。
JSP 页面脚本编制实际上就是 Java 编程。这只是部分正确的。自版本 1.0 以来,JSP 规范就已允许使用多种脚本语言。然而,这一功能并未得到广泛实现。IBM WebSphere 允许使用多种脚本语言,而且其底层技术是以开放源代码方式发布的(请参阅旁注 “替代解决方案的变通办法”)。此外,通过 JSP 标记库,Velocity 模板语言(Velocity template language)也是可用的。
JSP 页面不好,因为它允许进行脚本编制。这一抱怨在最近对 JSP 规范的修改中得到了解决。JSP 规范的当前发行版为标记库引入了一个新的概念。标记库可以包含一个验证方法,以对任何要使用该标记库的 JSP 页面进行验证。 在 2001 年科罗拉多软件峰会(Colorado Software Summit 2001)上,Mark Kolb 演示了一个简单的标记库验证器,这个标记库验证器验证了 JSP 页面完全可以不要脚本(请参阅 参考资料)。

上面列出的每一种误解都曾经至少是部分正确的。不过,JSP 技术是 J2EE 规范的主要部分而且得到了广泛使用。因此,会有很多厂商通过各种努力发展 JSP 技术并使之成熟,以解决用户所关心的问题。这是使用标准技术、而不使用专门的解决方案的好处之一。

虽然如此,在负责部署 Web 应用的经理们中间还是顽固地存在一种常见的思想。由于不了解误解背后的真相,又被告知 JSP 技术混合了编程和表示,这些经理们的第一反应是说他们将寻求 JSP 的替代解决方案,例如 Velocity。所以问题就是,像 Velocity 之类的解决方案真的能够解决问题吗?

Velocity 文档叙述说,它“允许任何人使用这种简单但强大的模板语言来引用 Java 代码中定义的对象”,文档还叙述说“Velocity 模板语言(VTL)旨在提供一种最容易、最简单而且最干净的方式,将动态内容结合到 Web 页面。即便是只有一点点或完全没有编程经验的 Web 页面设计人员,也应该很快就能够用 VTL 来将动态内容集成到 Web 站点。”换句话说,Velocity 并未将编程从表示中除去;而是要求用户学习一种新的专门的脚本语言。由于 Velocity 要求 Web 页面设计人员学习 Velocity 模板语言,它未能消除“内容和表示混合”的问题。

Velocity 只是市面上众多模板引擎的一个例子,但是,市面上的多数模板引擎,如 Velocity,都要求具有一定的前端编程技能。

JSP 技术的另一个流行的替代解决方案是 Cocoon 工程。Cocoon 是一种讨人喜欢的技术,它使用 XML 作为它的源数据格式。XSLT 转换的作用是将 XML 内容转换成适合于用户代理的格式,例如,适合于浏览器的 HTML。Cocoon 有它自己的称为 XSP 的动态页面格式。XSP 是 JSP 的近亲,但它工作在 Cocoon 环境中。XSL 会广泛存在的原因是,没有一种很好的办法将来自 JSP 页面的 XML 输出通过 Cocoon 发送到浏览器。早期 servlet 容器中的 servlet 链接机制无法胜任这一工作。不过,现在 Sun 已经引入了一种新的 servlet 过滤器机制,这种机制完全有能力将来自任意的 servlet 的 XML 输出通过一系列过滤器发送出去。从这个角度看,对 XSP 的需要明显降低了,需要 Cocoon 工程来提供其 XSLT 转换引擎作为 servlet 过滤器也变得明确起来。

关键的是,JSP 技术正在不断发展,以满足它的用户的需求。这意味着对 JSP 技术的任何评估都必须考虑到对其最新规范的审慎评论。即使是几个月前的评论和社论也会因出现了新的发展而显得过时(请参阅 参考资料)。

JSP 和定制标记库

JSP 不是一个工具,它是一个平台:一个基本的动态内容体系结构(工具在这个体系结构上构建)。虽然 JSP 平台确实包含有 scriptlet、声明和表达式,JSP 平台的缺省脚本语言也确实是 Java,但其它语言也为 JSP 规范及其日渐增多的实现所支持。此外,JSP 平台包含用于开发标记库的丰富的体系结构;任何可以用 HTML 或 XML 标记描述的东西都可以用标准 JSP 技术实现。

JSP 平台为了把前端内容表示连接到后端而引入了脚本元素,这确实违背了 Web 页面设计人员不必是程序员的前提。不过,好消息是这些脚本元素(声明、scriptlet 和表达式)只是权宜之计,而且事实上您根本不必使用它们(请参阅 参考资料)。脚本元素对程序员和快速建立原型都有好处,但它不是长久之计。定制标记库才是长久的解决之道。那些用于建立解决方案原型的脚本元素应作为开发过程中的一部分而迁移到标记库中。

设计定制标记库

定制标记库是 JSP 技术规范的版本 1.1 引入的。添加这种实用技术使程序员能够创建专门用于满足 Web 页面设计人员的需要的 XML 标记,而同时隐藏底层的实现细节。

总的来看,标记库的标记构成了一种语言:语法由 JSP 规范预定义为 XML,语素由标记库的 DTD 或模式定义,语义则由标记库提供。编写标记库时要理解的最重要的事情是“标记设计就是语言设计”。库设计人员的职责是确保由标记创建的语言适合于前端页面设计人员,前端页面设计人员必须能够理解和使用库的功能。

不幸的是,当前可用的绝大多数标记库都未能使其语义适合于非程序员。这些标记库是糟糕的语言设计的典型例子。它们使用了不当的语义和不适合于它们的目标用途的词汇。

标记设计的一个更好办法

问题的一部分源于程序员和设计人员的思考方式是根本不同的。大多数程序员都精通过程化编程模式。在过程化编程中,程序员的职责是,详细说明过程中的每一个步骤,告诉计算机如何完成所委托的任务。Web 页面设计人员以一种不同的模式工作。HTML 是一种 声明语言。Web 页面设计人员并不告诉计算机如何布置页面;相反,设计人员告诉计算机他想要什么:一张表格、一张图、一张有序列表,等等。Web 页面设计人员通常都不善于进行过程化编程,而且不熟悉过程化思考模式。许多 JSP 从业者之所以不鼓励在表示页面中使用(或者干脆不用) JSP 脚本元素,这是一个原因,虽然不是唯一的原因。

那么,如果您想设计一个标记库,该如何进行呢?如果我们都同意“Web 页面设计人员不是程序员”这种说法,那么我们也会同意说,把这样的标记库(这些标记库的语义是通过过程化编程加以体现的)提供给 Web 页面设计人员不会产生什么效益。这是重要的一点,但还有一点即使不算更重要,也是同样重要。当我们开始考虑 Web 页面设计人员的最有效方式时,我们就作了一个观念上的跳跃:我们开始在设计标记库时考虑最终用户 ― 客户。

谁是客户?

正如我在前面提到的,您在编写定制标记库时所用的观念是 标记设计就是语言设计。您或许对这一表述的稍有不同的变体很熟悉,这种变体源自面向对象编程。它说“库设计就是语言设计”。一个优秀的类库设计人员设计出来的类库使用的是从问题域派生而来的词汇。这是因为问题域易于被库的使用者理解。对于类库来说,库的用户,即它的客户,从定义上说是程序员。

对于标记库来说,其客户是非程序员。这意味着您正在设计一个产品,而您根本不是该产品领域的专家。与在任何开发工程中一样,您的第一个步骤因此应该是叫来一位或几位该领域的专家。与这些该领域的专家面谈,找出所要解决的真正问题,与这些专家合作,使用适合于该领域的词汇来设计一种标记语言。

我们正在介绍标记库作者所需的一个新的技能集:不是精通于编写标记库,而是编写适合于其目标用户(他们从定义上说是非程序员)的标记 语言的能力。我自己的观点是声明语言最适合于非程序员。

创建声明标记

大多数当前的标记库都集中于使用动词(动作)和副词(参数)来产生结果,而我倒愿意看到更多的标记设计人员使用名词(想要的结果)和形容词(修饰符)。这牵涉到对于很多程序员来说可能很难的正确的转变,但可以归结为一句话: 集中精力于“做什么”,而将“怎么做”隐藏起来

“做什么”,不是“怎么做”


页面设计应是一个描述您想要“做什么”而不是“怎么做”的过程。标记作者与他或她的客户 ― Web 页面设计人员 ― 之间的合作是以标记作者将 怎么做嵌入到标记中为中心的,以便客户可以简单地声明。这确实使标记作者在标记库设计阶段要做更多的工作,但所得到的语言的干净程度、可重用性和维护性则要好得多。声明语言也应尽量精简。

例如,考虑一个希望根据一天中的时间向查看者显示问候的页面。用 JSP scriptlet 编写的这样一个页面的代码可能是这个样子:

清单 1. 使用 scriptlet 的问候
Good
<%
     String timeOfDay = (new SimpleDateFormat("aa")).format(new Date());
     if (timeOfDay.equals("AM")) {
%>
Morning
<%
}else{
%>
Afternoon
<%
}
%>

另外一个使用了 Cocoon 的版本可能如清单 2 所示(用 Velocity 创建的版本的工作机制与此相似。)

清单 2. 使用 XSP 的问候
Good
<xsp:logic>
    String timeOfDay = ( new SimpleDateFormat("aa")).format(new Date());
      if (timeOfDay.equals("AM")) {
      <xsp:content>Morning</xsp:content>
    }
    else {
      <xsp:content>Afternoon</xsp:content>
    }
</xsp:logic>

清单 3 显示了用 JSP 和声明标记进行编码的页面的样子。

清单 3. 使用标记属性的问候
<p>
     Good
     <xyz:greeting am="Morning" pm="Afternoon" />
</p>

我们也可以选择对 JSP 标记语言进行扩展,以允许时间更精确的、内容任意的问候,如清单 4 所示。

清单 4. 使用子标记的问候
Good
<xyz:greeting>
       <xyz:when period="05:00 - 11:59">
       Morning
       </xyz:when>
       <xyz:when period="12:00 - 17:59">
       Afternoon
       </xyz:when
       <xyz:when period="18:00 - 20:59">
       Evening
       </xyz:when>
       <xyz:when period="21:00 - 04:59">
       Night
       </xyz:when>
</xyz:greeting>

语法上的转变

那么,当您开始用名词和形容词取代动词和副词进行思考时发生了什么呢?我们将用一个示例来说明。清单 5 显示了当我们设计一个“对要被循环的事物进行描述”的标记(而不是使用一个标记来描述“循环”动作)时发生什么。

清单 5. 一个用于循环的声明标记
<xyz:data-table source="iterator" id="beanname">
<tr>
<td><jsp:getProperty name="beanname" property="prop" /></td>
</tr>
</xyz:data-table>

清单 5 所使用的 data-table 标记对内容进行迭代,这些内容是迭代器 bean 预先放到页面环境中的,而且,data-table 标记还应用主体内容。所需的过程可以被隐藏,但所要得到的结果是显式的。迭代器接口的使用使得任何可迭代的数据,包括数据库游标,都可以通过使用桥接模式或适配器模式进行处理。这只是一个简单的示例,说明迭代可以是声明性的,而不是过程化的。一旦您理解了这个基本观点,就可以创建许多很好的扩展。

重申一次,您应该从问题域中派生出词汇,而不是从解决方案域中派生。这将帮您把注意力集中在适当的名词和形容词上。名词(标记)是来自问题域的实体,形容词(属性(attribute)或者可能是子标记)是属性(property)。干净、清晰而且简单的符号是好的符号。当您觉得有困难时,想想 HTML 标记;诸如 frame 集、table 和 form 这样的构件都是很好的示例。如果您发现自己是在描述过程而不是想要的结果,请退一步,重新考虑。您要一直把注意力集中在 做什么而不是 怎么做上。

使用 JSP 定制标记的优点

从上面的清单中,您可以注意到,使用定制标记的页面不指定实现。这是非常重要的一点。 清单 1清单 2所示的 scriptlet 和 XSP 解决方案将解决方案嵌入到了表示页面中。声明标记语言则不然。要理解为什么这是重要的一点,请考虑一下要算的是谁的时间。在两个脚本示例中,时间都是服务器的时间。但如果我们想要将页面更改成反映用户的时间,那该怎么办呢?基于 scriptlet 或 XSP 的页面将不得不重写。基于声明标记的页面则稍作修改以发出 JavaScript 就行了。如果我们想要选择是反映“服务器的时间”还是“客户机的时间”,那怎么办呢?如果我们使用 scriptlet 构建页面,则会很麻烦。如果我们使用的是声明标记语言,则我们可以只对标记语言作扩展,以允许以下内容(可选的):

<xyz:greeting machine="server or client">
...
</xyz:greeting>

当处理服务器的时间时,标记会提供正确的问候。当处理客户机的时间时,相同的标记会代之以生成 JavaScript 来提供正确的问候。

使用 JSP 定制标记的另一个重要优点出现在我们考虑表示逻辑的时候。有时人们在谈论表示和逻辑的分离,但他们真正在谈论的是表示逻辑与内容模型和业务逻辑的分离。表示逻辑需要在对视图产生的论述的适当级别上被编码。

例如,user-agent 标记应易于为 Web 页面设计人员所理解。声明性的 user-agent 标记可能会允许诸如清单 6 所示的那些构件。

清单 6. 样本 user-agent 标记
<xyz:user-agent agent="opera | ie 5+ | ns 4+">
... include contents only if the user-agent is one of the above ...
</xyz:user-agent>
<xyz:user-agent type="robot">
... include contents only if user-agent is categorized as a robot ...
</xyz:user-agent>
<xyz:user-agent supports="JavaScript">
... include contents only if user-agent supports JavaScript ...
</xyz:user-agent>

另外一种完成这类工作的方法可能是生成 XML,并让象 Cocoon 过滤器这样的程序根据 user-agent 采用 XSLT 变换。

结束语:瞄准可用性

本文的所有示例都未将注意力放在底层 Java 技术的工作机制上,甚至也未放在最终实现是发生在客户机还是服务器上。相反,着眼点是定制标记库技术对最终用户的可用性。

JSP 令人高兴的事情是,尽管易于使用是其目标,但我们并没有必要在实现方面作出牺牲。某些象这样看上去很简单的代码:

<xyz:input type="text" width="32" validate="[pattern]"/>

会发出客户机端的域验证代码。生成的页面可能会包含复杂的 JavaScript,但谁能知道呢?页面设计人员并不知道底层的复杂细节,它是由标记库生成的。

另一方面,“模式”这个专业术语确实应适合于页面设计人员。例如,这意味着正则表达式这一术语可能不是设计标记库时的最佳选择,尽管多数程序员都很喜欢它。不过,标记库 可以接受多种用于指定模式的形式。对各种可能性加以比较,然后作出能够提高标记的可用性的决定,这是设计定制标记库的基础部分。我想我已经证明 JSP 为标记的实现提供了相当多的选择;不过,与任何开发工程一样,设计定制标记库时第一个要考虑的应是最终用户。


相关主题

  • 您可以参阅本文在 developerWorks 全球站点上的 英文原文.
  • 请参加本文的 讨论论坛
  • 在 2001 年科罗拉多软件峰会上,演讲人 Mark Kolb演示了一个很好的示例,这个示例使用标记库验证,在 JSP 页面中贯彻了无脚本元素的策略。
  • Jakarta Struts工程是使用“模型-视图-控制器”类型模式的 JSP 页面的很好的例子。不过,这个模型的某些应用存在一些问题。例如,我发现大多数使用这种风格的交互作用的站点,我一旦登录进去就无法将任何有用的内容标上书签。这是因为提供给浏览器的唯一的 URL 也是负责控制的 servlet 的 URL。这个问题通常可以通过使用 servlet 映射解决。
  • 当谈到内容引擎的替代品时,我对 Cocoon 工程印象深刻,但我认为它应该改成在 servlet 过滤器中执行 XSLT 转换。
  • WebSphere Application Server实现了 JSP 技术,并且所实现的 JSP 技术支持多种脚本语言。
  • WebSphere 的 JSP 实现的底层技术,即 IBM 的 Bean 脚本编制框架已经以开放源代码的方式发布,而且最终应会被集成到 JSP 容器参考实现(它是 Jakarta 的一部分)中。
  • 请访问 Jakarta 主页以更多地了解 Jakarta 工程及其子工程,如 DBTags 标记库。
  • Brett McLaughlin 在“ JSP technology -- friend or foe?”(developerWorks,2000 年 10 月)中从与我的立场相反的角度讨论了 JSP 平台的替代解决方案。然而,他的仅一年前的那些抱怨,已经因为从那以后的 JSP 规范的改善而显得过时了,这是 JSP 技术一直在发展的证明。
  • Sue Spielman 的“ JSP vs. XSP”(OnJava.com,2001 年 2 月)对 JSP 技术和 Cocoon 之间的差异进行了彻底的讨论。正如 Spielman 所发现的,开发 XSP,部分是由于 Servlet 平台未能提供一种有效的方式从内容生成器通过转换器链接。开发 XSP 部分是为了使用 Cocoon 的内部链接机制来弥补该缺陷。然而,此后不到一年,一种称为“Servlet 过滤”(Servlet Filtering)的标准技术就使得人们不再需要专用链接机制了。
  • Spielman 还写了一篇关于创建定制标记库的 技术性介绍(OnJava.com,2001 年 4 月)。
  • 通过阅读我的文章“ Dynamic Web-based data access using JSP and JDBC technologies”(developerWorks,2001 年 9 月),您可以了解到关于 DBTags 标记库(以及 JSP 和 JDBC 技术)的更多信息。
  • 想要了解一些完全不同的内容,请阅读 Alex Roetter 的评论 JSP Explorer(developerWorks,2001 年 8 月),JSP Explorer 是基于 JSP 的工具,用于让开发者能够快速地测试简单的想法和生成软件文档。
  • 您可在 IBM developerWorks Java 技术专区找到数百篇关于 Java 编程的各个方面的文章。

评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Java technology, Web development
ArticleID=53251
ArticleTitle=JSP 标记库:着意设计的更好的可用性
publish-date=12072001