| 2007 年 4 月 24 日 Ruby on Rails 開(kāi)發(fā)和 Java? 開(kāi)發(fā)有著(zhù)本質(zhì)的不同。在跨越邊界 系列的最后一期中,Bruce Tate 將概述使用 Rails 從頭開(kāi)發(fā)一個(gè)復雜、可伸縮的 Web 站點(diǎn)時(shí)所發(fā)現的二者的主要差異。 Rails 開(kāi)發(fā)人員常常把 Java 開(kāi)發(fā)人員看作是沉悶而勞碌的老古董。而 Java 崇拜者則常常認為 Ruby on Rails 只是一個(gè)玩具,根本不能用于任何嚴肅的軟件開(kāi)發(fā)。作為一名對這兩種技術(shù)都有著(zhù)廣泛使用經(jīng)驗的顧問(wèn),我認為真實(shí)的情形介于這兩種觀(guān)點(diǎn)之間。由于跨越邊界 系列文章即將結束,因此我打算對它們再作一次比較。本文并非考察某種特殊的技術(shù)或語(yǔ)言,而是主要介紹我當前正在從事的項目,并將它與我以前從事的 Java 項目進(jìn)行比較。另外,我建議您閱讀“跨越邊界”系列的前幾期文章,對相關(guān)主題作更深入的了解。這種直接的說(shuō)明可讓您在二者之間權衡利弊,并可能使您在數據庫 Web 應用程序 green-field 的開(kāi)發(fā)中通過(guò)使用 Rails 獲益。
不論是 Ruby on Rails 框架還是任何 Java 框架都不能解決所有問(wèn)題。為了提高成功的幾率,需要長(cháng)期、細致地考察業(yè)務(wù)問(wèn)題,理解周?chē)母鞣N假定情況,以及了解您的團隊。只有這樣才能選出正確的語(yǔ)言來(lái)進(jìn)行開(kāi)發(fā)。 去年,Arvato Systems 聘請我帶領(lǐng)團隊構建 ChangingThePresent.org,它是一種新平臺,用于將非贏(yíng)利性團體和捐贈人組織在一起。與很多 Internet 公司一樣,我們向客戶(hù)展示了可購買(mǎi)的實(shí)際產(chǎn)品。與其他公司不同的是,這些 “產(chǎn)品” 指的是提供機會(huì ),比如:一名癌癥研究員一小時(shí)服務(wù)收費 50 美元、幫助盲人收費 30 美元,或者保護一英畝雨林一個(gè)月收費 20 美元。我們面臨兩大挑戰:一份很緊湊的時(shí)間表和長(cháng)期的復雜性。 開(kāi)發(fā)工作從九月份開(kāi)始,要求必須在十一月份之前建立起一個(gè)站點(diǎn),以便有機會(huì )趕上圣誕節期間的通信量高峰。(最終我們超出了十一月份這個(gè)期限兩星期。)在其他開(kāi)發(fā)語(yǔ)言中,基于 Java 的解決方案可能要花費 6 到 18 個(gè)月才能完成這一任務(wù)。因此生產(chǎn)力是一個(gè)很重要的考慮因素,這與 Java 部署思想相悖。 通過(guò)對競爭對手和項目的考察,我們了解到:我們希望能夠每天給站點(diǎn)帶來(lái)幾百萬(wàn)次點(diǎn)擊的通信量。而我們需要每天有幾十萬(wàn)次成功的點(diǎn)擊,因此可伸縮性也是一個(gè)考慮因素。這與 Java 部署思想相符。 最后,我們了解到:發(fā)布初始站點(diǎn)只是一個(gè)開(kāi)始。我們只實(shí)現了總體規劃的百分之三。因此我們所選的技術(shù)需要根據復雜性和負載作出一些調整。我認為 Ruby 語(yǔ)言在復雜性方面會(huì )更易于調整,因為它提供了對更高級語(yǔ)言和特性(如開(kāi)放類(lèi))的支持,以及具有更少配置需求和更簡(jiǎn)單、集成化編程模型的 Rails 框架。 雖然我們面臨著(zhù)時(shí)間和可伸縮性方面的挑戰,但是也擁有一些其他的有利因素。我們擁有一張完全空白的候選名單:可以選擇想要的任何技術(shù)、任何團隊??梢远x項目、培訓和全部的技術(shù)。我們可完全自由地作出選擇。 Java 語(yǔ)言是一種優(yōu)秀的通用語(yǔ)言。它總是應用于新的技術(shù)領(lǐng)域,如嵌入式系統和移動(dòng)設備。Java 語(yǔ)言在廣泛關(guān)注的集成方面也表現優(yōu)秀。它具有高性能、流行并受到平臺的良好支持。但是正如您在本系列中所了解到的那樣,Java 語(yǔ)言并不是用于數據庫支持的 Web 應用程序的 green-field 開(kāi)發(fā)的最佳選擇(請參閱文章 “動(dòng)態(tài)類(lèi)型語(yǔ)言中的 Web 開(kāi)發(fā)策略”)。 相比之下,Ruby on Rails 框架則是新的。并沒(méi)有很多人使用 Rails 開(kāi)發(fā)高通信流量的站點(diǎn),并且幾乎不存在什么使用 Rails 多年開(kāi)發(fā)項目的經(jīng)驗。但它仍然是一種高生產(chǎn)力的數據庫支持的 Web 應用程序開(kāi)發(fā)的框架。最后,盡管我們對 Rails 缺乏開(kāi)發(fā)長(cháng)期項目的經(jīng)驗和并未得到廣泛部署有所顧慮,但那份緊湊的時(shí)間表驅使我們選擇了 Ruby on Rails。 作出這個(gè)決定之后,我們發(fā)現招募項目人才非常容易。我們還發(fā)現早期的生產(chǎn)力優(yōu)勢非常顯著(zhù) —— 甚至比預期的還要好。我們確實(shí)遇到了一些早期的穩定性問(wèn)題,因此加強了測試工作的力度(請參閱文章 “在集成框架中進(jìn)行測試,第 1 部分” 和 “在集成框架中進(jìn)行測試,第 2 部分”)。此后穩定性得到了極大的改進(jìn)。
每個(gè)框架設計者都使用一組假定來(lái)構造該框架的重寫(xiě)原理。學(xué)習遵守該原理的約束可使您愉快地進(jìn)行編程,而挑戰這些約束則會(huì )使您的編程受挫。Rails 框架和 Java 框架擁有很多不同的原理。 Rails 是一種集成框架,需要使用高度利用 Ruby 語(yǔ)言的動(dòng)態(tài)本質(zhì)(請參閱 “Ruby on Rails 的秘笈是什么? ”)。Rails 開(kāi)發(fā)人員強調框架的生產(chǎn)力特性而不是工具特性,并且常常將 Web 架構看得非常簡(jiǎn)單,在本系列的前幾篇文章中您已了解到這一點(diǎn)。Java 設計者通常必須分塊地組合開(kāi)發(fā)環(huán)境,獨立地選擇持久性、Web 和集成層。他們通常嚴重地依賴(lài)工具來(lái)簡(jiǎn)化核心任務(wù)。Web 架構設計趨向于較為復雜。 Java 框架往往是解決一個(gè)小問(wèn)題(比如持久性或查看組織),而 Rails 則是一個(gè)集成環(huán)境。Rails 開(kāi)發(fā)人員的優(yōu)勢在于不必解決與許多不同框架集成的問(wèn)題。大多數 Hibernate 開(kāi)發(fā)人員陷入了過(guò)早關(guān)閉與 Java Web 框架之間的連接的陷阱。Rails 視圖框架是從頭構建的,以便與 ActiveRecord 集成(Rails 持久性框架)。當您考察用于 Web 服務(wù)、配置和插件的 Rails 框架時(shí)也會(huì )發(fā)現類(lèi)似的經(jīng)驗。Java 編程支持各種不同的框架,對于所有這些框架使用不同的集成策略。 Java 開(kāi)發(fā)人員的優(yōu)勢在于選擇。如果您需要從頭構建一個(gè)框架,則可能要考慮使用基于 SQL 的解決方案用于數據庫集成(如 iBATIS 或 Java 編程中基于 JDBC 的包裝框架之一)。反過(guò)來(lái),如果要使用一種古老的模式進(jìn)行編程,則可能要使用對象關(guān)系映射框架(如 Hibernate)。相比之下,如果您使用 Rails,則擁有一個(gè)主要選擇:ActiveRecord。這意味著(zhù) Java 框架提供了更多的選擇,有時(shí)能提供更好的集成開(kāi)發(fā)項目的解決方案。但是由于我們要開(kāi)發(fā)一個(gè) green-field 項目,因此選擇算不上是一個(gè)問(wèn)題。 Rails 原理的下一個(gè)主要部分是動(dòng)態(tài)編程語(yǔ)言(請參閱文章 “Java 模型以外的類(lèi)型策略”)。Java 工具往往可以有效地使用 Java 類(lèi)型模型提供的額外信息。工具可以識別錯誤和有效地重構代碼。Rails 還可有效地利用編程語(yǔ)言的優(yōu)點(diǎn)。Ruby 是一種構建特定于域的語(yǔ)言(DSL)的理想語(yǔ)言(請參閱文章 “活動(dòng)記錄和 Java 編程中特定于域的語(yǔ)言”)。Rails 集中使用 DSL 來(lái)完成從構建模型對象之間的關(guān)系到指定自定義組件(如狀態(tài)機器或可上傳的圖像)的所有工作。動(dòng)態(tài)語(yǔ)言常常更加簡(jiǎn)潔,因此 Rails 項目比 Java 項目要簡(jiǎn)練得多,可讓用戶(hù)更簡(jiǎn)練地表達代碼和配置。在 ChangingThePresent.org 項目中,我們發(fā)現技術(shù)頂尖的程序員可達到更高的生產(chǎn)力,但是我們確實(shí)需要招募經(jīng)驗更豐富的開(kāi)發(fā)人員。我對這種妥協(xié)非常滿(mǎn)意。 傳統的 Java 程序員對 IDE 有著(zhù)近乎虔誠的熱愛(ài),造成這一現象有充分的理由。IDE 提供了語(yǔ)法的完整性檢查、修正了小錯誤并提供了增量編譯以便更快地完成編碼、編譯、部署和測試這樣的周期。最近幾年來(lái),開(kāi)發(fā)環(huán)境開(kāi)始更好地利用編譯循環(huán)和靜態(tài)類(lèi)型提供的信息。IDE 現在編輯抽象語(yǔ)法樹(shù)(AST),而不是(或者同時(shí))編輯代碼的文本表示。這一策略允許使用強大的代碼重構工具,而使用靜態(tài)類(lèi)型語(yǔ)言的同樣方法來(lái)實(shí)現此功能則困難得多。 靜態(tài)類(lèi)型確實(shí)能更好地使用工具,但是也存在缺點(diǎn)。強制使用靜態(tài)類(lèi)型通常需要編譯器,而編譯步驟必然會(huì )降低生產(chǎn)力。使用 Rails,我可以更改一行代碼并重新加載瀏覽器,就可立即看到更改的結果。與 Java 開(kāi)發(fā)人員相比,大多數 Ruby 開(kāi)發(fā)人員只使用一種很好的編輯器。TextMate 是最流行的 Ruby on Rails 編輯器,它提供了語(yǔ)法突出顯示、代碼完整性檢查,以及一些頻繁使用的結構的良好的模板支持。而當發(fā)現可將所有簡(jiǎn)單的基于 Ruby 的腳本(用作基本的 Rails 工具包)放入編輯器中時(shí),您會(huì )更加喜出望外。與純粹的調試器不同的是,我可以使用斷點(diǎn)腳本,該腳本可停止特定的應用程序,進(jìn)入一個(gè) Ruby 解釋程序,我可在其中調用方法、檢查變量的值,以及甚至在恢復執行之間修改代碼。 傳統的 Web 端 Java 架構包括:一個(gè)用于域對象和數據訪(fǎng)問(wèn)對象的層、一個(gè)提供業(yè)務(wù)級 API 的外觀(guān)層、一個(gè)控制器層和一個(gè)視圖層。此架構比典型的 “模型-視圖-控制器” 架構(使用 Smalltalk 語(yǔ)言最早創(chuàng )建)稍微復雜一些。相比之下,Ruby on Rails 包括一個(gè)使用 ActiveRecord 設計模式的模型層、一個(gè)控制器層和一個(gè)視圖層。我們喜歡易于獲得的 Rails 方法。它更加簡(jiǎn)練并且帶來(lái)額外的復雜性和錯誤的機會(huì )更小。 Java 框架通??梢宰杂傻厥褂?XML 配置,而 Rails 主要使用慣例來(lái)避免可能的配置。在程序員必須指定配置的位置,Rails 通常依賴(lài) Ruby(常常以 DSL 形式)來(lái)提供配置。對于 green-field 開(kāi)發(fā),我發(fā)現慣例優(yōu)先于配置是很有意義的。該策略為我省去了很多行代碼,更簡(jiǎn)化了必須編寫(xiě)的代碼。估計我們所需的配置只有傳統 Java 應用程序中所指定的十分之一。我們有時(shí)會(huì )損失一點(diǎn)靈活性,但這并不足以抵消使用此策略帶來(lái)的節省。 總而言之,Rails 框架的原理適合解決 ChangingThePresent.org 項目中的問(wèn)題。集成的各種工具讓我可以利用框架實(shí)現更多的功能而無(wú)需自己進(jìn)行過(guò)多的集成?!皯T例優(yōu)先原則” 為我節省了配置站點(diǎn)的時(shí)間。動(dòng)態(tài)語(yǔ)言為經(jīng)驗豐富的開(kāi)發(fā)人員提供了更多的能力和靈活性,同時(shí)也使他們能夠利用更少的代碼表達更強大的思想。該框架適合于我們團隊的能力和要解決的業(yè)務(wù)問(wèn)題。
Java 和 Ruby 語(yǔ)言的最流行的持久性框架可以比任何其他特性更好地闡明 Java 和 Ruby 經(jīng)驗之間的區別,Java 開(kāi)發(fā)人員通常使用 Hibernate,它是一種對象關(guān)系映射框架。通過(guò) Hibernate,您可獲取現有的模型和模式并使用注釋或 XML 表達二者之間的映射。Hibernate 類(lèi)是簡(jiǎn)單傳統 Java 對象(POJO),它的每個(gè)對象派生自一個(gè)通用的基類(lèi)。大多數配置是顯式的,使用注釋、XML 或二者的某種結合。 而 ActiveRecord 是一種包裝的框架,就是說(shuō)每個(gè)類(lèi)都是現有類(lèi)的包裝器(請參閱文章 “研究活動(dòng)記錄”)。ActiveRecord 根據關(guān)聯(lián)表的內容(如表中每列的一個(gè)屬性)自動(dòng)地向模型對象添加特性。所有的類(lèi)都從一個(gè)通用的基類(lèi)繼承。ActiveRecord 主要利用通用約定來(lái)推斷配置。例如:
對象關(guān)系映射是使用遺留模式(可能定義時(shí)沒(méi)有考慮對象模型)時(shí)的最佳解決方案。但是當您能為應用程序顯式地設計數據庫模式時(shí),您通常不需要映射框架了。我們將 ActiveRecord 看作我們的一個(gè)巨大優(yōu)點(diǎn)。我們可以包含關(guān)系數據庫,需要時(shí)轉入 SQL 并在適當的時(shí)候退出。 Rails 遷移使我們能夠用代碼表示模式的兩個(gè)版本之間的差別,和它們所包含的數據之間的差別(請參閱文章 “Rails 遷移”)。對每個(gè)遷移都進(jìn)行了命名和編號??稍谌魏螘r(shí)候恢復到任何版本。遷移有以下一些確切的優(yōu)點(diǎn):
但是遷移也有一些限制。如果兩個(gè)開(kāi)發(fā)人員同時(shí)創(chuàng )建遷移,則編號會(huì )出現混亂,所以我們必須手動(dòng)處理。我們通過(guò)有效的通信來(lái)使這些問(wèn)題最小化:團隊成員構建需使用遷移的新模型時(shí)發(fā)出通知。但是這個(gè)模型依賴(lài)于團隊的開(kāi)發(fā)人員較少或遷移進(jìn)展較慢的情況。 ActiveRecord 還有其他的限制,其中一些是故意作出的。Rails 的創(chuàng )建者認為:數據庫的約束和組成應歸入應用程序而不是數據庫,這種思想帶來(lái)了一些副作用。ActiveRecord 使用視圖的情況不是很好:構建過(guò)程(克隆模式、復制測試數據并運行測試)并不能正確地進(jìn)行復制。ActiveRecord 在使用參考完整性約束的某些場(chǎng)合也會(huì )出現問(wèn)題,因為某些類(lèi)型的關(guān)聯(lián)可能連接到多個(gè)數據庫表??缭綇碗s模型進(jìn)行預先加載很復雜,通常在連接多行時(shí)需要使用 SQL。繼承也受到限制:使用 ActiveRecord 時(shí),我被迫使用單表繼承 映射策略,而該策略并不總是最佳選擇。(參見(jiàn) 參考資料) 所有的持久性策略都充滿(mǎn)了妥協(xié)。我認為 ActiveRecord 實(shí)現了一組有效的妥協(xié),常常選擇了簡(jiǎn)單性??偠灾?,ActiveRecord 和遷移是我們的積極推動(dòng)。我們可以快速地構建解決方案,我們擁有足夠的 SQL 訪(fǎng)問(wèn)權可在需要時(shí)改進(jìn)系統性能。但是當 ActiveRecord 并不總能應對挑戰時(shí),最好將 Rails 應用于使用老舊模式的項目。一些替代的持久性模型正在出現,包括 RBatis,一種 iBATIS Java 框架的端口(參見(jiàn) 參考資料)?,F在討論 RBatis 的有效性還為時(shí)過(guò)早。
對于我的團隊和項目來(lái)說(shuō),Ruby on Rails 被證明相當有效。我還不知道這個(gè)項目的最終規模如何,因為撰寫(xiě)本文時(shí)該系統才運行 3 個(gè)月?,F在只是開(kāi)始增加通信量。但是我們對生產(chǎn)力卻很了解。我知道團隊的預算比競爭公司(這些公司常常使用 Java 解決方案)的要低得多。我對我們的生產(chǎn)力也很有信心。 通過(guò)跨越邊界 系列,我向您介紹了 Java 領(lǐng)域以外的語(yǔ)言和解決方案。但程序員畢竟是技術(shù)人員。每個(gè)高明的技術(shù)人員的工具包中都應包含適用于每個(gè)解決方案的廣泛的工具集。除工具外,本系列中介紹的觀(guān)點(diǎn)也為您展示了一些其他思路?,F在一些框架設計者甚至將 Seaside、Rails 中的技術(shù)甚至 JavaScript 應用于 Java 框架中。找機會(huì )進(jìn)行同樣的應用,繼續 “跨越邊界”。 學(xué)習
獲得產(chǎn)品和技術(shù)
討論
| |||||||||||||||||||||
聯(lián)系客服