跨越邊界: 動態類型語言中的 Web 開發策略

跨越邊界: 動態類型語言中的 Web 開發策略

超越 JSP

developerWorks
文檔選項
<script type="text/javascript" language="JavaScript"> </script>
將此頁作爲電子郵件發送

將此頁作爲電子郵件發送



級別: 初級

Bruce Tate ([email protected]), 總裁, RapidRed

2006 年 7 月 28 日

過 去十年的大部分時間裏,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 頁面的一些方法有了一些認識。下一次,我將會探討使用函數語言解決通用問題的不同方法。



參考資料

學習
  • 您可以參閱本文在 developerWorks 全球站點上的 英文原文

  • Beyond Java (Bruce Tate, O'Reilly, 2005年):本文作者所著的書,內容涉及 Java 語言的興起和興盛,以及能夠在某些領域挑戰 Java 平臺的技術。

  • The Problems with JSP”(Jason Hunter,Servlets.com,2000年 6 月):一篇歷史悠久的文章,講述有關 JSP 技術的問題。

  • Ruby:一種卓越的用於表示模板的語言。

  • Seaside:此框架使您能夠用 Smalltalk 開發複雜的 Web 應用程序。

  • 層疊樣式表 (CSS):一項用於爲 Web 文檔添加諸如字體、顏色及空格等樣式元素的機制,該機制由萬維網聯盟開發。

  • ERb:一個用於處理 CGI 腳本的 Ruby 類,也可以被用在 Ruby 應用程序中以轉化 Ruby 模板。

  • eruby:用 C 語言寫成的一個 eRuby 實現。

  • From Java To Ruby: Things Your Manager Should Know(Pragmatic Bookshelf,2006年):作者作著的書,講述了在何時何處將 Java 編程轉換成 Ruby on Rails 是有意義的,以及如何做出這種改變。

  • Programming Ruby(Pragmatic Bookshelf,2005年):Ruby 編程方面的暢銷書。

  • RIFE, Wicket, Spring Web MVC, Jakarta Tapestry:使用模板策略的 Java Web 開發框架。

  • JRuby:JRuby 是用 100% 純 Java 語言寫成的與 Ruby 1.8.2 版兼容的 Ruby 解釋器。

  • Java 技術專區:數百篇有關 Java 編程各方面的文章。

獲得產品和技術
  • Ruby:從該項目的 Web 站點獲取 Ruby。

  • Ruby on Rails:下載開放源碼的 Ruby on Rails Web 框架。

  • Seaside:下載截然不同的 Smalltalk Web 框架。



關於作者

Bruce Tate 居住在德克薩斯州的首府奧斯汀,他是一位父親,同時也是山地車手和皮艇手。他是三本 Java 暢銷書的作者,包括榮獲 Jolt 大獎的 Better, Faster, Lighter Java。他最近又出版了 Beyond Java 一書。他在 IBM 工作了 13 年,現在是 RapidRed 顧問公司 的創始人,在這裏他潛心研究基於 Java 技術和 Ruby on Rails 的輕量級開發策略和架構。

 

http://www.ibm.com/developerworks/cn/java/j-cb07056/index.html
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章