轉載: http://www.5itjob.net/html/86/86_itemid_1183.html
Ruby on Rails 是一個相對較新的 Web 應用程式架構,構建在 Ruby 語言之上。它被宣傳為現有企業架構的一個替代,而它的目標,簡而言之,就是讓生活,至少是 Web 開發方面的生活,變得更輕鬆。在本文中,Aaron Rustad 將對 Rails 和傳統的 J2EE 架構在架構上的一些關鍵特性進行比較。
Ruby on Rails 是一個 Web 應用程式架構,它的目標是為應用程式開發提供一條易行道。實際上,架構的支援者們聲稱 Ruby on Rails 開發人員的生產率最多是使用傳統 J2EE 架構的 10 倍。(請閱讀“Rolling with Ruby on Rails”一文,以獲得關於這一聲明的更多內容;請參閱 參考資料)。雖然這句話造成了 Rails 和 J2EE 社區相當大的爭議,但爭論中卻很少談及如何比較 Rails 和 J2EE 架構。本文將使用公司專屬應用程式程式中常見的開源工具,對 Rails 架構和典型的 J2EE 實現進行比較。
什麼是 Ruby on Rails?
要想找到用一句話描述 Rails 的簡單說明,只需查看項目的 首頁 即可:
Rails 是一個用 Ruby 編寫的全棧的(full-stack)、開源的 Web 架構,可以使用它來輕鬆編寫實際的應用程式,所需的代碼也要比大多數架構花在處理 XML 上的代碼少。
雖然我不能保證架構確實會提供它所承諾的輕鬆快樂,但是上面這句話確實很好地總結了 Rails 的品質。全棧包括:Web 服務器、處理 HTTP 要求和響應的架構,以及方便地把資料持久儲存到關係資料庫的架構。Rails 通過消除複雜的 XML 設定檔,使用 Ruby 語言的動態性質,協助把靜態類型語言中常見的許多重複代碼減少到最少,努力使開發工作變得更容易。
Rails 和典型的 J2EE Web 堆棧
圖 1 比較了 Rails 堆棧和典型的 J2EE Web 堆棧(包括 Tomcat servlet 容器、Struts Web 應用程式架構和 Hibernate 持久性架構)。
圖 1. Rails 和 J2EE 堆棧的比較
可以看到,Rails 堆棧和構成普通的基於 J2EE 的 Web 應用程式的組件之間的基本區別很小。兩者都有用來執行應用程式代碼的容器;都有協助分離應用程式的模型、視圖和控制項的 MVC 架構;以及持久儲存資料的機制。
MVC 架構 模型-視圖-控制器(MVC)是應用時間相當長、應用面相當廣的一個設計模式。它源自 Smalltalk;如今,幾乎所有的 G使用者介面架構,包括 Web 和胖用戶端,都以該架構為基礎。MVC 有三個部分:模型,負責商務邏輯,包括應用程式狀態和將在這個狀態上執行的動作;視圖,用來渲染和向使用者呈現模型(在 Web 應用程式中,視圖一般渲染為 HTML);控制器,定義應用程式的行為。有關 MVC 模式的詳細解釋,請參閱 參考資料。 |
前端控制器
Struts 的 ActionServlet
和 Rails 的 DispatchServlet
都是前端控制器模式的例子;所以,它們提供了相同的功能。它們接受 HTTP 要求,解析 URL,把請求的處理轉寄給適當的動作。在 Struts 中,動作是擴充自 Action
的類;對於 Rails,動作是擴充自 ActionController
的類。兩個前端控制器之間的主要區別是它們如何決定處理具體請求的動作。
使用 Struts,開發人員需要把特定請求的映射外部化到 XML 設定檔中的 Action
類。當首次裝入 ActionServlet
時,它將解析這個檔案,並準備接受請求。根據約定,以 .do
結束的請求被重新導向到 ActionServlet
,由 ActionServlet 指派到適當的 Action
。圖 2 的 XML 是一個典型的映射。它告訴 ActionServlet
把叫作 deleteOrder.do
的請求轉寄到 controllers.order.DeleteOrderAction
作進一步處理。
Rails 採用了不同的方式。它沒有依賴設定檔把請求映射到某一個動作,而是根據請求的 URL 發現適當的動作。從圖 2 可以看到,URL http://localhost/order/delete/4
告訴 Rails 調用 OrderController
執行個體上的 delete
方法,並將 4
作為可用的執行個體變數。Rails 足夠聰明,知道 /order
將映射到檔案 order_controller.rb 中定義的一個控制器類。如果在控制器中定義了 find
方法,那麼只要用 find
替代 URL 中的 delete
,就可以調用這個方法。
圖 2. Rails 和 Struts 中的 URL 對應
動作和模型
在 Rails 和 Struts 中,動作用來充當前端控制器和模型之間的橋樑。開發人員提供動作的現實,從而提供特定於應用程式的請求處理。前端控制器負責接受請求,並把請求傳遞到特定動作。圖 3 示範了 Rails 和 Struts 基本的動作階層。
圖 3. Rails 和 Struts 的動作階層
動作是模型還是控制器?
Action 和 ActionController 從技術上講是 MVC 模式的控制器的一部分,因為它們對客戶發起的事件進行響應。但是,在小型應用程式中,開發人員通常在這些類中對域或商務邏輯進行編碼,所以在這些情況下,也可以把它們看作是模型的一部分。最佳實務建議:應當把域邏輯從控制器中抽象出來,放置在它自己的特定於域的類中。 |
Struts 要求開發人員擴充 Action
並覆蓋 execute()
,以處理請求。通常,每個 Action
類都提供了非常具體的工作單元。圖 3 示範了三個特定動作:SaveOrderAction
、DeleteOrderAction
和 ListOrdersAction
。前端控制器將調用 execute()
方法,傳遞給它許多有用的對象,其中包括 HTTP 要求和響應對象。ActionForm
是一個類,它可以方便地向視圖來回傳輸並驗證與表單有關的輸入,ActionMapping
包含映射的配置資訊,就像 圖 2 的 XML 所描述的那樣。
execute()
方法返回 ActionForward
對象,Struts 用這個對象來確定對請求繼續進行處理的組件。一般來說,這個組件是一個 JSP 頁面,但是 ActionForward
也能指向其他動作。開發人員必須清楚,Struts 建立的是 Action
的單一執行個體,並允許多個線程調用它的 execute()
。這使請求處理變得更快,因為架構處理每個請求時不用頻繁地建立新的 Action
執行個體。但是因為可以在多個線程之間共用單一對象,所以必須遵守適當的線程注意事項,因為其他線程可能會破壞在這個動作中保持狀態的執行個體變數。
在 Rails 中,必須擴充 ActionController::Base
,讓模型參與到請求處理中。Rails 沒有將 ActionController
的執行個體池化;相反,它為每個請求建立新的執行個體。雖然這對效能可能有負面影響,但是它可以讓開發變得更容易。開發人員不需要關注 Struts 中存在的線程問題,因此,會話、請求、標題和參數都可以作為 ActionController
的執行個體成員來進行訪問。ActionController
還是一個將特定域邏輯的所有處理組合在一起的合理場所。Struts 的 Action
類是細粒度的,它提供了非常具體的工作單元,而 Rails ActionController
則是粗粒度的,它將具體的工作單元類比為一些方法。
清單 1 和 清單 2 分別示範了典型的 Struts 動作和典型的 Rails 動作
表 1 提供了對兩種方法的邏輯流程的比較,並示範了清單 1 和清單 2 的特定行中發生的事情。研究 DeleteOrderAction
的 execute()
方法和 OrderController
的 delete
方法,可以看出它們基本上是相同的。
表 1. execute() 和 delete 方法比較
步驟 |
Struts |
Rails |
架構調用動作 |
行 03: execute() |
行 07: delete |
從請求中檢索到的 ID |
行 06-07:從請求對象中取出 |
行 08:從所有參數的執行個體雜湊中取出 |
從資料庫刪除訂單記錄 |
行 09、14-24:調用 delete() 方法,用 Hibernate 刪除記錄 |
行 09:用 ActiveRecord 刪除記錄 |
重新導向到列出剩餘訂單 |
行 11:用 ActionMapping 對象尋找將要轉寄處理的下一個組件。圖 2 中的 XML 映射顯示,success 將映射到 /listOrders ,這是另一個 Action ,負責尋找剩餘訂單,並以 JSP 的形式呈現它們 |
行 10:用將調用的下一動作的雜湊來調用 redirect_to 方法;在這種情況下,它只是調用同一控制器的 list 方法 |
持久性架構
持久性架構 用來在應用程式層和資料庫之間來回移動資料。Hibernate 和 Rails 的持久性架構可以歸類為對象/關係映射(ORM)工具,這意味著它們接受資料的物件檢視,並將該視圖映射到關聯式資料庫內的表中。使用兩種架構的目的都是為了減少與關聯式資料庫有關的開發時間。但是,圖 4 示範了兩者在設計和配置上的一些根本區別。
圖 4. Active Record 和 Hibernate 持久性架構的比較
圖 4. Active Record 和 Hibernate 持久性架構的比較
Hibernate
Hibernate 基於 Data Mapper 模式,在這種模式中,特定的映射器類 Session
負責在資料庫中持久儲存和檢索資料。Hibernate 可以持久儲存任何 Java 對象,只要這個對象符合 JavaBean 規範。XML 對應檔描述了如何將類映射到資料庫中具體的表,並描述了類與其他類的關係。
清單 3 顯示了 Hibernate 對應檔的一個執行個體。class
標籤把 Order
對象映射到 ORDERS
表,還有許多子標籤用於描述其屬性、ID 訂單名稱,以及同 models.Item
的一對多關聯性。清單 4 顯示了 Order
類本身。
清單 3. Order.hbm.xml
...01 <hibernate-mapping>02 <class name="models.Order" table="ORDERS"03 dynamic-update="true" dynamic-insert="false"04 discriminator-value="null">0506 <id name="id" column="id" type="java.lang.Long" 07 unsaved-value="null">08 <generator class="identity"/>09 </id>10 11 <set name="items" lazy="false" inverse="false"12 cascade="none" sort="unsorted">13 <key column="id"/>14 <one-to-many class="models.Item"/>15 </set>16 17 <property name="name" type="java.lang.String"18 update="true" insert="true"19 access="property" column="name"/>20 </class>21 </hibernate-mapping>
|
清單 4. Order.java
01 public class Order {02 private Set items;03 private String name;04 private Long id;05 06 public Long getId() { return id;}07 08 public void setId(Long id) { this.id = id;}09 10 public Set getItems() { return items;}11 12 public void setItems(Set items) { this.items = items; }13 14 public String getName() { return name; }15 16 public void setName(String name) { this.name = name; }17 }
|
Active Record
反射和元編程 Wikipedia 中(請參閱 參考資料)簡要地把 反射 定義為“程式在啟動並執行時候檢查和修改其進階結構的能力”。在那裡,還將 元編程 定義為“編寫那些能夠編寫和操作其他其他程式(或它們自己),將其他程式作為自己的資料的程式,或者編寫那些完成其他程式在運行時所做的部分工作的程式。” 以下代碼將實現反射:
01 obj = "some_string"02 if obj.respond_to?('length'):03 puts "obj length = #{obj.length}" 03 end>> obj length = 5
|
這個代碼將實現元編程:
01 class SomeClass02 end03 newMethod = %q{def msg() puts "A message!" end}04 SomeClass.class_eval(newMethod)05 aClass = SomeClass.new06 aClass.msg>> A message!
|
|
Rails 的 ORM 架構叫作 Active Record,它基於同名的設計模式。Martin Fowler 將 Active Record 描述為“封裝資料庫表或視圖中資料行的對象,封裝資料庫訪問,在資料上添加域邏輯”。在 Rails 中,每個域對象都將擴充提供 CRUD 操作的 ActiveRecord::Base
。
與 Hibernate 一樣,Active Record 不需要對應檔;實際上,使用 Active Record 的開發人員不需要對 getter 或 setter、甚至類的屬性進行編碼。通過一些漂亮的詞彙分析,Active Record 能夠判斷出,Order
類將映射到資料庫中的 ORDERS
表。使用 Ruby 反射和元編程的組合,表的列可以變成對象的屬性。訪問器和調整器也添加了進來。
清單 5 顯示了 Order
類的完成後的代碼。在 Order
類體中有一行代碼定義了它與 Item
對象的關係。has_many
是一個靜態方法調用,符號 :items
是它的參數。ActiveRecord 用 :items
發現 Item
域對象,然後將這個 Item
對象映射回資料庫中的 ITEMS
表。
清單 5. order.rb
01 class Order < ActiveRecord::Base02has_many :items03 end
|
像 清單 5 那樣編碼的 Order
類在運行時提供了一些類和執行個體方法。表 2 提供了可在 Order
上使用的操作和屬性的部分列表:
表 2. 在 Order 上可用的屬性和操作
類方法 |
執行個體方法 |
屬性 |
find(*args)
find_by_sql(sql)
exists?(id)
create(attributes)
update(id, attributes)
update_all(updates, conditions
delete(id)
delete_all(conditions)
- ...
|
add_items
build_to_items
create_in_items
find_all_in_items
find_in_items
has_items?
items
items=
items_count
remove_items
|
|
結束語
雖然 Ruby on Rails 是一個非常新、令人興奮的架構,並且在 Web 社區中已經引起了人們相當的興趣,但是它的核心架構仍然遵循在 J2EE 中發現的基本模式。開發把兩個架構分開的 Web 應用程式是一種合理的方法。Rails 更喜歡清楚的代碼而不是設定檔,而 Ruby 語言的動態性質在運行時產生了大部分管道 代碼。大多數 Rails 架構都是作為獨立項目建立的,而且應用程式開發能夠從一組同類組件受益。相比之下,典型的 J2EE 堆棧傾向於構建在通常獨立開發的最好的組件之上,常常用 XML 進行配置並將組件組合在一起。
那麼,是否應該考慮對下一個 Web 應用程式使用 Rails 呢?嗯,為什麼不呢?它是編寫得很好的組件堆棧,它們彼此之間工作得很好,並且基於行業接受的企業模式。Ruby 語言支援快速開發,並通過生產大多數應用程式管道來添加到架構。熟悉 Java 世界中的 MVC 和 ORM 架構的人們在用 Rails 表達自己的思想時沒有任何困難。
與 J2EE 一起分發會不會有利於 Rails?絕對不要。J2EE 是一個已經設定好的標準,有許多固定的實現,而且,最重要的是,它是一個經過驗證的技術。我建議您下載一份 Rails 的副本,並開始自己鑽研它。許多可用的教程都是介紹性的,這些教程可以讓您立即開始使用 Rails。再次聲明,我並不能保證您會通過使用 Rails 得到快樂,但是我敢打賭您會感到滿意。