標籤:
始終使用 MVC 架構。
MVC 架構可以將商務邏輯(Java beans 和 EJB 組件)、控制器邏輯(Servlets/Struts 動作)、展示層(JSP、XML/XSLT)清晰地分離開來。良好的分層可以帶來許多好處。
MVC 架構對於成功使用 J2EE 是如此重要,以致沒有其他最佳實務可以與其相提並論。模型-視圖-控制器(MVC)是設計 J2EE 應用程式的基礎。MVC 將您的程式碼簡單地劃分下面幾個部分:
負責商務邏輯的代碼(即模型??通常使用 EJB 或者普通的 Java 對象來實現)。
負責使用者介面顯示的代碼(即視圖??通常通過 JSP 及標記庫來實現,有時也使用 XML 和 XSLT 來實現)。
負責應用程式流程程的代碼(即控制器??通常使用 Java Servlet 或像 Struts 控制器這樣的類來實現)。
如果您不遵循基本的 MVC 架構,在開發過程中就會出現許多的問題。最常見的問題就是在視圖部分添加了太多的成分,例如,可能存在使用 JSP 標記來執行資料庫訪問,或者在 JSP 中進行應用程式的流程式控制制,這在小規模的應用程式中是比較常見的,但是,隨著後期的開發,這樣做將會帶來問題,因為 JSP 逐步變得越來越難以維護和調試。
類似地,我們也經常看到將視圖層構建到商務邏輯的情況。例如,一個常見的問題就是將在構建視圖時使用的 XML 解析技術直接應用到業務層。業務層應該對業務對象??而不是綁定到視圖的特定資料表示進行操作。
然而,只是具有合適的組件並不一定意味著可以使您的應用程式得到合適的分層。我們常常見到一些應用程式套件組合含 servlet、JSP 和 EJB 組件所有這三項,然而,其主要的商務邏輯卻是在 servlet 層實現的,或者應用程式導航是在 JSP 中處理的。您必須進行嚴格的代碼檢查並重構您的代碼,以確保應用程式的商務邏輯只在模型層(Model layer)進行處理,應用程式導航只通過控制器層(Controller layer)進行處理,而您的視圖(Views)只是將傳遞過來的模型對象以 HTML 及 JavaScript 的形式表示出來。
在應用程式的每一層都使用自動單元測試和測試管理。
不要只是測試您的圖形化使用者介面(GUI)。分層的測試使測試及維護工作變得極其簡單。
在過去的幾年中,在方法學領域有了相當大的革新,例如新出現的被稱為 Agile(例如 SCRUM [Schwaber] 和極限編程 [Beck1])的輕量級方法現在已經得到了很普遍的應用。幾乎所有的這些方法中的一個共同的特徵是它們都提倡使用自動的測試載入器,這些工具可以協助開發人員用更少的時間進行迴歸測試 (regression testing),並可以協助他們避免由於不充分的迴歸測試造成的錯誤,因此可以用來提高程式員的工作效率。實際上,還有一種被稱為 Test-First Development [Beck2] 的方法,這種方法甚至提倡在開發實際的代碼之前就先編寫單元測試。然而,在您測試代碼之前,您需要將代碼分割成一些可測試的片斷。一個"大泥球"是難以測試的,因為它不是只實現一個簡單的易於識別的功能。如果您的每個代碼片斷實現多個方面的功能,這樣的代碼將難以保證其完全的正確性。
MVC 架構(以及 J2EE 中的 MVC 實現)的一個優點就是元素的組件化能夠(實際上,相當的簡單)對您的應用程式進行單元測試。因此,您可以方便地對實體 bean、會話 bean 以及 JSP 獨立編寫測試案例,而不必考慮其他的代碼。現在有許多用於 J2EE 測試的架構和工具,這些架構及工具使得這一過程更加簡單。例如,JUnit(是一種由 junit.org 開發的開放原始碼工具)和 Cactus(由 Apache 開發的開放原始碼工具)對於測試 J2EE 組件都非常有用。[Hightower] 詳細探討了如何在 J2EE 中使用這些工具。
儘管所有這些詳述了怎樣徹底地測試您的應用程式,但是我們仍然看到一些人認為只要他們測試了 GUI(可能是基於 Web 的 GUI,或者是獨立的 Java 應用程式),則他們就全面地測試了整個應用程式。GUI 測試很難達到全面的測試,有以下幾種原因。首先,使用 GUI 測試很難徹底地測試到系統的每一條路徑,GUI 僅僅是影響系統的一種方式,可能存在後台運算、指令碼和各種各樣的其他訪問點,這也需要進行測試。然而,它們通常並不具有 GUI。第二,GUI 級的測試是一種非常粗粒度的測試。這種測試只是在宏觀水平上測試系統的行為。這意味著一旦發現存在問題,則與此問題相關的整個子系統都要進行檢查,這使得找出 bug(缺陷)將是非常困難的事情。第三,GUI 測試通常只有在整個開發週期的後期才能很好地得到測試,這是因為只有這那個時候 GUI 才得到完整的定義。這意味著只有在後期才可能發現潛在的 bug。第四,一般的開發人員可能沒有自動的 GUI 測試載入器。因此,當一個開發人員對代碼變更時,沒有一種簡單的方法來重新測試受到影響的子系統。這實際上不利於進行良好的測試。如果開發人員具有自動的代碼級單元測試工具,開發人員就能夠很容易地運行這些工具以確保所做的更改不會破壞已經存在的功能。最後,如果添加了自動構建功能,則在自動構建過程中添加一個自動的單元測試工具是非常容易的事情。當完成這些設定以後,整個系統就可以有規律地進行重建,並且迴歸測試幾乎不需要人的參與。
另外,我們必須強調,使用 EJB 和 Web 服務進行分布式的、基於組件的開發使得測試單個的組件變得非常必要。如果沒有"GUI"需要測試,您就必須進行低級(lower-level)測試。最好以這種方式開始測試,省得當您將分布式的組件或 Web 服務作為您的應用程式的一部分時,您不得不花費心思重新進行測試。
總之,通過使用自動的單元測試,能夠很快地發現系統的缺陷,並且也易於發現這些缺陷,使得測試工作變得更加系統化,因此整體的品質也得以提高。
按照規範來進行開發,而不是按照應用伺服器來進行開發。
要將規範熟記於心,如果要背離規範,要經過慎密的考慮後才可以這樣做。這是因為當您背離規則的時候,您所做的事情往往並不是您應該做的事情。
當您要背離 J2EE 可以允許您做的事情的時候,這很容易讓使您遭受不幸。我們發現有一些開發人員鑽研一些 J2EE 允許之外的東西,他們認為這樣做可以"稍微"改善J2EE的效能,而他們最終只會發現這樣做會引起嚴重的效能問題,或者在以後的移植(從一個廠商到另一個廠商,或者是更常見的從一個版本到另一個版本)中會出現問題。實際上,這種移植問題是如此嚴重,以致 [Beaton] 將此原則稱為移植工作的基本最佳實務。
現在有好幾個地方如果不直接使用 J2EE 提供的方法肯定會產生問題。一個常見的例子就是開發人員通過使用 JAAS 模組來替代 J2EE 安全性,而不是使用內建的遵循規範的應用程式伺服器機制來進行驗證和授權。要注意不要脫離 J2EE 規範提供的驗證機制,如果脫離了此規範,這將是系統存在安全性漏洞以及廠商相容性問題的主要原因。類似地,要使用 servlet 和 EJB 規範提供的授權機制,並且如果您要偏離這些規範的話,要確保使用規範定義的 API(例如 getCallerPrincipal())作為實現的基礎。通過這種方式,您將能夠利用廠商提供的強安全性基礎設施,其中,業務要求需要支援複雜的授權規則。
其他常見的問題包括使用不遵循 J2EE 規範的持久性機制(這使得交易管理變得困難)、在J2EE程式中使用不適當的J2SE 方法(例如線程或 singleton),以及使用您自己的方法解決程式到程式(program-to-program)的通訊,而不是使用 J2EE 內在支援的機制(例如 JCA、JMS 或 Web 服務)。當您將一個遵循 J2EE 的伺服器移植到其他的伺服器上,或者移植到相同伺服器的新版本上,上述的設計選擇將會造成無數的問題。唯一要背離規範的情況是,當一個問題在規範的範圍內無法解決的時候。例如,安排執行定時的商務邏輯在 EJB2.1 出現之前是一個問題,在類似這樣的情況下,我們建議當有廠商提供的解決方案時就使用廠商提供的解決方案(例如 WebSphere Application Server Enterprise 中的 Scheduler 工具),而在沒有廠商提供的解決方案時就使用第三方提供的工具。如果使用廠商提供的解決方案,應用程式的維護以及將其移植到新的規範版本將是廠商的問題,而不是您的問題。
最後,要注意不要太早地採用新技術。太過於熱衷採用還沒有整合到 J2EE 規範的其他部分或者還沒有整合到廠商的產品中的技術常會帶來災難性的後果。支援是關鍵的??如果您的廠商不直接支援一種特定的在 JSR 中提出的技術,但此技術還沒有被 J2EE 所接受,那麼您就不應該採用此技術。畢竟,我們中的大多數人從事解決業務問題,而不是推進技術的發展。
從一開始就計劃使用 J2EE 安全性。
啟用 WebSphere 安全性。這使您的 EJB 和 URL 至少可以讓所有授權使用者訪問。不要問為什麼??照著做就是了。
在與我們合作的客戶中,一開始就打算啟用 WebSphere J2EE 安全性的顧客是非常少的,這一點一直讓我們感到吃驚。據我們估計大約只有 50% 的顧客一開始就打算使用此特性。例如,我們曾與一些大型的金融機構(銀行、代理等等)合作過,他們也沒有打算啟用安全性。幸運的是,這種問題在部署之前的檢查時就得以解決.
不使用 J2EE 安全性是危險的事情。假設您的應用程式需要安全性(幾乎所有的應用程式都需要),您敢打賭您的開發人員能夠構建出自己的安全性系統,而這個系統比您從 J2EE 廠商那裡買來的更好。這可不是個好的賭注,為分布式的應用程式提供安全性是異常困難的。例如,您需要使用網路安全加密令牌控制對 EJB 的訪問。以我們的經驗看來,大多數自己構建的安全性系統是不安全的,並且有重大的缺陷,這使產品系統極其脆弱。
一些不使用 J2EE 安全性的理由包括:擔心效能的下降,相信其他的安全性(例如 Netegrity SiteMinder)可以取代 J2EE 安全性,或者是不知道 WebSphere Application Server 安全特性及功能。不要陷入這些陷阱之中,尤其是,儘管像 Netegrity SiteMinder 這樣的產品能夠提供優秀的安全特性,但是僅僅其自身不可能保護整個 J2EE 應用程式。這些產品必須與 J2EE 應用程式伺服器聯合起來才可能全面地保護您的系統。
其他的一種常見的不使用 J2EE 安全性的原因是:基於角色的模型沒有提供足夠的粒度存取控制以滿足複雜的商務規則。儘管事實是這樣的,但這也不應該成為不使用 J2EE 安全性的理由。相反地,應該將 J2EE 驗證及 J2EE 角色與特定的擴充規則結合起來。如果複雜的商務規則需要做出安全性決策,那就編寫相應的代碼,其安全性決策要基於可以直接使用的以及可靠的 J2EE 驗證資訊(使用者 ID 和角色)。
建立您所知道的
反覆的開發工作將使您能夠逐漸地掌握所有的 J2EE 模組。要從建立小而簡單的模組開始而不是從一開始就馬上涉及到所有的模組。
我們必須承認 J2EE 是龐大的體系。如果一個開發小組只是開始使用 J2EE,這將很難一下子就能掌握它。在 J2EE 中有太多的概念和 API 需要掌握。在這種情況下,成功掌握 J2EE 的關鍵是從簡單的步驟開始做起。
這種方法可以通過在您的應用程式中建立小而簡單的模組來得到最好的實現。如果一個開發小組通過建立一個簡單的領域模型以及後端的持久性機制(也許使用的是 JDBC),並且對其進行了完整的測試,這會增強他們的自信心,於是他們會使用該領域模型去掌握使用 servlet 和 JSP 的前端開發。如果一個開發組發現有必要使用 EJB,他們也會類似地開始在容器管理的持久性 EJB 組件之上使用簡單的會話 Facades,或者使用基於 JDBC 的Data Access Objects(JDBC-based Data Access Objects,DAO),而不是跳過這些去使用更加複雜的構造(例如訊息驅動bean和JMS)。
這種方法並不是什麼新方法,但是很少有開發組以這種方式來培養他們的技能。相反地,多數開發組由於嘗試馬上就構建所有的模組,同時涉及 MVC 中的視圖層、模型層和控制器層,這樣做的結果是他們往往會陷入進度的壓力之中。他們應該考慮一些敏捷(Agile)開發方法,例如極限編程(XP),這種開發方法採用一種增量學習及開發方法。在 XP 中有一種稱為 ModelFirst 的過程,這個過程涉及到首先構建領域模型作為一種機制來組織和實現使用者情境。基本說來,您要構建領域模型作為您要實現的使用者情境的首要部分,然後在領域模型之上構建一個使用者介面(UI)作為使用者情境實現的結果。這種方法非常適合讓一個開發組一次只學到一種技術,而不是讓他們同時面對很多種情況(或者讓他們讀很多書),這會令他們崩潰的。
還有,對每個應用程式層重複的開發可能會包含一些適當的模式及最佳實務。如果您從應用程式的底層開始應用一些模式如Data Access Objects和會話 Facades,您就不應該在您的JSP和其他視圖對象中使用域邏輯。
最後,當您開發一些簡單的模組時,在開始的初期就可以對您的應用程式進行效能測試。如果直到應用程式開發的後期才進行效能測試的話,這往往會出現災難性的後果。
當使用 EJB 組件時,始終使用會話 Facades。
決不要將實體 bean 直接暴露給任何使用者類型。對實體 bean 只可以使用本地 EJB 介面(Local EJB interfaces)。
當使用 EJB 組件時,使用一個會話 Facades 是一個確認無疑的最佳實務。實際上,這個通用的實踐被廣泛地應用到任何分布式的技術中,包括 CORBA、EJB 和 DCOM。從根本上講,您的應用程式的分布"交叉地區"越是底層化,對小塊的資料由於多次重複的網路中繼造成的時間消耗就越少。要達到這個目的的方法就是:建立大粒度的 Facades 對象,這個對象包含邏輯子系統,因而可以通過一個方法調用就可以完成一些有用的業務功能。這種方法不但能夠降低網路開銷,而且在 EJB 內部通過為整個業務功能建立一個事務環境也可以大大地減少對資料庫的訪問次數。
EJB 本地介面(從 EJB 2.0 規範開始使用)為共存的 EJB 提供了效能最佳化方法。本地介面必須被您的應用程式顯式地進行訪問,這需要代碼的改變和防止以後配置 EJB 時需要應用程式的改變。由於會話 Facades 和它包含的整個 EJB 對於彼此來說都應該是本地的,我們建議對會話 Facades 後面的實體 bean 使用本地介面。然而,會話 Facades 本身的實現(典型例子如無狀態會話 bean)應該設計為遠程介面。
為了效能的最佳化,可以將一個本地介面添加到會話 Facades。這樣做利用了這樣一個事實:在大多數情況下(至少在 Web 應用程式中),您的 EJB 用戶端和 EJB 會共同存在於同一個 Java 虛擬機器(JVM)中。另外一種情況,如果會話 Facades 在本地被調用,可以使用 J2EE 應用伺服器配置最佳化(configuration optimizations),例如 WebSphere 中的"No Local Copies"。然而,您必須注意到這些可供選擇的方案會將互動方法從按值傳遞(pass-by-value)改變為按引用傳遞(pass-by-reference)。這可能會在您的代碼中產生很微妙的錯誤。當您要利用這些方案時,您應該在一開始就考慮其可行性。
如果在您的會話 Facades 中使用遠程介面(而不是本地介面),您也可以將同樣的會話 Facades 在 J2EE 1.4 中以相容的方式作為 Web 服務來配置。這是因為 JSR 109(J2EE 1.4 中的 Web 服務部署部分)要求使用無狀態會話 bean 的遠程介面作為 EJB Web 服務和 EJB 實現的介面。這樣做是值得的,因為這樣做可以為您的商務邏輯增加用戶端類型的數量。
最重要的J2EE最佳實務