跨越边界: 动态类型语言中的 Web 开发策略

发表于:2007-05-24来源:作者:点击数: 标签:跨越动态类型边界中的
过去十年的大部分时间里,Java 社区一直在使用 JavaServer Pages (JSP) 技术,但现在已开始显现出衰退的迹象。目前,长期以来的惯例阻止了 Java 程序员 在 Web 页面中使用 Java 代码,并且即便扩展简单的组件也很繁琐。超越 JSP 编程的 Java Web 开发框架已经
过去十年的大部分时间里,Java™ 社区一直在使用 JavaServer Pages (JSP) 技术,但现在已开始显现出衰退的迹象。目前,长期以来的惯例阻止了 Java 程序员Web 页面中使用 Java 代码,并且即便扩展简单的组件也很繁琐。超越 JSP 编程的 Java Web 开发框架已经出现,但却缺少动态语言功能。本文将向您展示 Ruby 的 Web 页面开发策略并会提及有关 Seaside 的基本方法。

在 Java Web 开发的早些时候,Sun 和 Microsoft 曾经争夺过有关构建动态 Web 页面的实际标准的控制权。Sun 公司引入了 Servlet API。通过 servlet,您可以用 Java 语言的所有功能快速生成一个 Web 页面。具备编译 servlet 能力的免费 servlet 容器实现 (Apache Tomcat) 的存在使得 servlet 变得非常流行。与之相对的,Microsoft 公司引入了称作 Active Server Pages (ASP) 的 API。该技术很容易:您可以快速掌握并创建更多的高级 Web 页面,其中包括一些具有数据库支持或其他动态内容的页面。

关于本系列

跨越边界 系列中,作者 Bruce Tate 提出了这样一个观点:如今的 Java 程序员可以通过学习其他方法和语言得到很好的其他思路。自从 Java 技术明显成为所有开发项目的最佳选择以来,编程前景已经改变。其他框架正影响着构建 Java 框架的方式,从其他语言中学到的概念也影响着 Java 编程。您编写的 Python(或 Ruby、或 Smalltalk 等) 代码可以改变您处理 Java 编码的方式。

本系列向您介绍与 Java 开发截然不同但可直接应用于 Java 开发的编程概念和技术。在一些情况下,需要集成该技术以对其进行利用。而在另一些情况下,则可以直接应用这些概念。单独的开发工具并不重要,重要的是这种思想,即能够影响 Java 社区中开发人员、框架,甚至基本方法的其他语言和框架。

首先来说说 JSP 技术。JSP 被设计来直接与 ASP 竞争(顾名思义)。使用 JSP 技术,您可以使用标记构建一个 Web 页面并将 Java 代码直接放入页面中。JSP 容器将 JSP 文件编译成 servlet 形式。然后 servlet 引擎像执行其他 servlet 一样执行该页面。JSP 和 ASP 一样,都实现了一个基于模板的方式。模板可以像您想要在用户浏览器中所呈现的那样,帮助您构建一个简单的 Web 页面。当模板引擎处理简单的占位符元素时,您可以用值、组件或作为页面一部分的结构来替换这些元素。尽管 Java 平台拥有众多具有通用目的的模板引擎,但 JSP 已经占领了绝对的市场份额,部分原因在于大量 ASP 开发人员转换来使用 JSP。

比起 Java 语言或 ASP,一些其他的语言在处理 Web 开发方面表现要强得多。了解一下动态语言中相互竞争的方法,会让您更加清楚在 Java 平台上能够使用哪些方法。在本文中,我讨论了 Ruby 中代码生成是如何工作的,并且深入讲解了 Seaside 中一种更加根本的基于组件的方法。

Ruby 模板

Ruby 模板依赖此语言的简单功能来为 Web 开发提供一种简单却行之有效的方法。通过掌握一些层次化的概念即可快速理解 Ruby 模板,这些概念每一层要比其上一层更加强大。

Ruby 模板最基础的部分是 String。Ruby 字符串是一级对象。当您在 Ruby 中创建一个字符串时,您既能用单引号,也能用双引号把它括起来。清单 1 展示了一些例子,即您可在 Ruby 解释器中键入来创建字符串的三条不同命令:


清单 1. 创建一个 Ruby 字符串
irb(main):001:0> "This is a string.".class
            => String
            irb(main):002:0> 'This is also a string.'.class
            => String
            irb(main):003:0> String.new('This is yet another string.').class
            => String
            

Ruby 不处理任何用单引号括起来的字符串信息。如果用双引号括起来,Ruby 会在运行代码的时候进行替换。清单 2 是一个使用换行符的示例。使用单引号时,Ruby 保持换行符不变。使用双引号时,则会解释换行符:


清单 2. 单引号与双引号的比较
irb(main):004:0> puts 'Use \n to specify a new line in Ruby.'
            Use \n to specify a new line in Ruby.
            => nil
            irb(main):005:0> puts "A \\n causes a line break \n like this."
            A \n will cause a line break
            like this.
            => nil
            

在第二个 puts 语句中(puts 代表 put string),Ruby 处理了两次替换。单个 \ 后跟一个字符表示不在键盘上的特殊字符,如 \n 代表换行。正如反斜号本身有特殊的意义一样,在一个字符前的 \ 也有着特殊的意义,即转义该字符,这同在 Java 语言中一致。清单 2 处理了 \\\n 两个替换。

#{any_expression} 是一个更有趣的替换命令。如果您使用双引号将含有此命令的 String 括起来,Ruby 将返回此字符串,用 any_expression 的值取代 #{any_expression}。清单 3 给出了一个例子:


清单 3. 简单变量替换
irb(main):006:0> name = "Elvis"
            => "Elvis"
            irb(main):007:0> puts "Your name is #{name}"
            Your name is Elvis
            => nil
            

您已经做好了创建一个基本的模板所需的绝大部分准备。只差一步即可完成简单的模板处理。您可以通过在具有单引号的字符串中使用 #{} 替换来推迟赋值。稍后,通过给模板加双引号并调用 eval() 方法,您可以随时将模板与变量绑定,如清单 4 所示:


清单 4.推迟赋值
irb(main):008:0> template = 'Your name is #{name}'
            => "Your name is \#{name}"
            irb(main):009:0> name = gets
            Elvis
            => "Elvis\n"
            irb(main):011:0> puts eval('"' + template + '"')
            Your name is Elvis
            => nil
            

注意,template 在初始化 name 前就已经存在了。您可以使用这个简单的模板策略来处理非常简单的 Web 页面,但通常您还需要用到更多知识。包含简单编码结构(如含有动态数据的表循环)的能力形成了大多数动态 Web 页面的主干。

用 eRuby 嵌入代码

在 HTML 页面中嵌入 Ruby 代码的典型方法(包括在 Rails 中使用的方法)是一个被称作 eRuby 的过滤器。现有的几个实现中包括了一个被称作 ERb 的基于 Ruby 的实现,和一个被称作 eruby(参见 参考资料)的速度更快且基于 C 语言的实现。它们每一个都能作为 Web 服务器的插件运行,但在便利地处理本地文件时,您通常会使用 ERb,而当需要更快的速度时,例如对于生产用的 Web 服务器,您会使用 eruby。eRuby 过滤器处理文本文件时,除了下列三种情况以外都会完整地保持原文本:

  • <% 和 %> 之间的文本是 Ruby 代码,eRuby 将如实执行该代码。
  • <%= 和 %> 之间的文本是 Ruby 表达式。eRuby 执行该表达式并用其返回的值替换整个表达式。
  • 一行中以单个 % 开头的内容,将被作为 Ruby 代码执行。

例如在清单 5 中名为 test.rhtml 的文件(按照惯例,.rhtml 是针对 eRuby 标记的、包含有 HTML 的文件的扩展名)。清单 5 使用了 Ruby 代码和表达式。请注意 do 循环体中的文本:


清单 5.一个简单的 rhtml 文件
<% 4.times do |i|%>
            <h1>This code is inside the loop.</h1>
            <p>This line is pass number <%= i %> through the loop.</p>
            <% end %>
            

您可以简单地通过 ERb 运行此代码,如清单 6 所示:


清单 6.用 ERb 处理文件
  > erb test.rhtml
            <h1>This code is inside the loop.</h1>
            <p>This line is pass number 0 through the loop.</p>
            <h1>This code is inside the loop.</h1>
            <p>This line is pass number 1 through the loop.</p>
            <h1>This code is inside the loop.</h1>
            <p>This line is pass number 2 through the loop.</p>
            <h1>This code is inside the loop.</h1>
            <p>This line is pass number 3 through the loop.</p>
            





回页首


调用模板

Ruby 是一系列处理基于 LAMP 的 Web 开发的语言之一。LAMP 代表:

  • Linux
  • Apache
  • MySQL
  • 一种开放源码的 P-语言:Python、Perl 或 PHP。(如果不考虑基础的话,Ruby 是一种名义上的 P-语言。)

在 LAMP 中,通常您可以通过简单的操作系统脚本,使用诸如公共网关接口 (CGI) 之类的框架来提供 Web 应用程序。

eRuby 有效地提升了典型的基于 CGI 编程的水平。正如您所预料的那样,您可以直接将 ERb (或是基于 C 语言的 eruby 的等价物)插入 Apache HTTP 服务器并调用 Web 页面。随后您可以将 Ruby 视为 Java servlet 等价物了。但新的框架常将 ERb 作为一个 Ruby 库来调用。如果想要从 API 中创建一个 Ruby 模板,您应该使用清单 5 的修正版本,如清单 7 所示:


清单 7. 在 Ruby 应用程序中运行 ERb 模板
require 'erb'
            template = ERB.new <<-EOF
            <% 4.times do |i|%>
            <h1>This code is inside the loop.</h1>
            <p>This line is pass number <%= i %> through the loop.</p>
            <% end %>
            EOF
            puts template.result
            

通过这种方式执行这些模板,您将 Ruby 编程语言下的所有能力赋予了您的模板。随后您可以在模板中的代码和应用程序其余部分的代码之间实现无缝重构。例如,您可以创建一个帮助器来基于散列映射的内容构建简单的表,如清单 8 所示:


清单 8. 创建帮助器
require 'erb'
            def table_helper(map)
            body = map.collect do |item|
            "<tr><td>#{item[0]}</td>
            <td>$#{item[1]}</td></tr>\n"
            end
            return("<table>\n#{body}</table>")
            end
            map = {
            "Peaches" => "1.95",
            "Apples" => ".95"
            }
            template = ERB.new <<-EOF
            <p>Here's our price list</p>
            <%= table_helper(map) %>
            EOF
            puts template.result
            

给定一个散列映射,清单 8 显示了一个用于创建表的帮助器。本例中先声明一个散列映射和一个模板,然后把它们放在一起使用(通常您可以将这个模板放到一个单独的文件中;而为了保持清单的简单,我将其写作一行)。注意,您会很自然地在 Ruby 编程语言的功能的基础上扩展模板系统的功能。本例将 ERb 方式的替换与提供简单字符串的基本 Ruby 字符串替换结合了起来。Ruby on Rails 用以下几种强制的方法将这些替换方式组合在一起:

  • Rails 的 partials 使小的子表单能够更好地被重用和组织。
  • Rails 的 layouts 使包含标题、菜单栏和版权信息在内的公共元素能够在所有页面中重用。
  • Rails 的 helpers,类似于清单 8 中的帮助器,使简化和重用视图结构变得简单。

事实上,Rails 的设计为用户贡献库提供了简单的方式,这些库使用 JavaScript 来呈现诸如 Ajax 元素、树控制或弹出式菜单之类的复杂特性。Rails 社区提供了功能远远强大于 Rails 基本功能的帮助器。





回页首


Seaside 策略

Seaside 是一个不使用传统模板的 HTML 呈现策略的例子,是针对使用 Smalltalk 开发 Web 应用程序的框架。如今,Web 开发人员通过一个名为层叠样式表 (CSS)(参见 参考资料)的策略将样式放到了一个更加重要的位置。在 CSS 中,您可以使用模板友好的语言来构建包括呈现位置、颜色、背景以及字体在内的所有的样式信息。Seaside 在样式上非常依赖 CSS,但却从不用模板来呈现基本组件。

在 Seaside 中,每一个组件都能够自我呈现。最基本的情况是,一个组件仅仅显示基本的 HTML 标记和必要的动态内容以呈现此组件。例如,清单 9 定义了 renderContentOn 方法来创建一个简单 Seaside 计数器:


清单 9.一个简单的 Seaside 组件
renderContentOn: t1
            t1 heading: count.
            t1
            anchorWithAction: [self increase]
            text: '++'.
            t1 space.
            t1
            anchorWithAction: [self decrease]
            text: '--'
            

在清单 9 中,t1 heading: count. 使用实例变量 count 中的内容呈现了一个标题,该变量包含了一个简单计数器的值。然后,代码呈现了一个在 Seaside 术语中称作的链接,该链接包含了 '++' 文本并调用 increase 方法。最后,代码中针对 decrease 方法呈现了一个相似的链接。图 1 展示了含有一个在清单 9 中呈现的计数器的 Web 页面:


图 1. 一个简单的 Seaside 组件
一个简单的 Seaside 组件

更加复杂的组件能够使其子组件自我呈现。用这种方法,Seaside 能够用最小的努力产生相当复杂的组件。例如,表能够呈现一系列行,而这些行又能够依次呈现一系列单元格。清单 10 是一个复杂组件的例子:


清单 10. 一个更加复杂的 Seaside 组件
renderContentOn: t1
            counters
            do: [:t2 | t1 render: t2]
            separatedBy: [t1 horizontalRule]
            

清单 10 中的代码呈现了一组计数器,遍历一个容器集合,为每一个容器调用 render 方法,并用水平线将每个容器分隔。您可以在图 2 中看到结果:


图 2. 一组 Seaside 计数器
一组 Seaside 计数器

Seaside 模型不使用模板,却可以依靠每个个体对象的自我呈现来构建异常强大的具有复杂交互功能的 Web 页面。Seaside 依靠样式表来维持设计人员与编程人员间的良好关系。分层、布局、回溯和样式在 Seaside 中是家常便饭。





回页首


超越 JSP

在 Jason Hunter 六年前撰写的一篇出色的文章中,他列举了 JSP 的缺点(参见 参考资料)。我解释为:

  • JSP 比它需要的要难。
  • 循环很困难。
  • 错误消息不友好。
  • 在编程人员和设计人员之间保持平衡太难了。

多年来,Java 开发人员已经提出了改变这些不足的实践和框架。JavaServer 页面标准标记库 (JSTL)(JSP编程的自定义标记系统)试图简化从 JSP 文件中分解出代码以及将代码放入共享库。但那个标记系统远比其需要的复杂。它深具诱惑,却不能直接将 Java 代码插入 Web 页面,因而 Java Web 页面开发人员通过避免所有的代码来抵制这种诱惑,这像避免瘟疫一样。几个建立在 JSP 技术上的框架试图简化此问题。更新的框架则试图改善 Java Web 开发体验。例如,JavaServer Faces (JSF) 试图重新发明 Struts —— 但其最初却使用了 JSP(这违背了一些专家的意见)。

在最近的几年,一些不依赖于 JSP 的创新性框架已经开始出现。通过依靠更简单的模板哲学(一种更接近于纯 HTML 语言的思想),RIFE、Wicket、Spring Web MVC、Tapestry 以及其他一些框架都声称为组件的集成提供了更简单的策略(参见 参考资料)。它们都可以让 Web 开发人员使用本机设计工具而非为 JSP 编程专门定制的工具。这对 Java 语言来说是个健康的方向。然而,其他语言,如 Ruby 在处理 Web 开发上比 Java 技术要好得多。

能将 Java 丰富的企业功能同简易的动态语言结合起来的可行性策略是使用嵌入式虚拟机。依据 JRuby 的领袖 Thomas E. Enebo 的说法,开发人员已经在研究一种 servlet,这种 servlet 允许嵌入 Ruby 代码来生成 Ruby 风格的 Web 页面。在很多方法中,这一种能够让您利用到这两者的优势。

我希望您对其他语言呈现 Web 页面的一些方法有了一些认识。下一次,我将会探讨使用函数语言解决通用问题的不同方法。

原文转自:http://www.ltesting.net

评论列表(网友评论仅供网友表达个人看法,并不表明本站同意其观点或证实其描述)