歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux綜合 >> Linux資訊 >> Linux文化

使用Translator模式構建更好的網站


Donald S Bell
IBM 的 IT 專家
2001 年 2 月


本文介紹 Translator 模式,並說明如何在 JSP 技術和 servlet 環境中使用 Translator 模式。讀完本文以後,您將能夠利用本文提供的代碼示例成功實現這一模式。

在使用 JSP 文件和 servlet 構建 Web 應用程序時,應用程序的界面多半會是 HTML。浏覽器翻譯後的 HTML 就是一個大型字符串。構成應用程序的業務對象只有少數屬性為字符串,其余屬性則為日期、數字甚至其他業務對象。在構建 Web 應用時,如何將業務對象所包含的信息轉換為浏覽器可識別的 HTML 是個大問題。幾乎每個應用程序都會以 HTML 格式收集信息,而這些信息又會作為字符串發送給服務器。因此現在還存在如何將所提交的信息轉換為業務對象可識別的值這一問題。

Translator 模式通過提供一個與 JSP 文件、servlet 和業務對象協同工作的 Translator 對象解決了這兩個問題。Translator 對象將三個不同對象結合在一起,從而使每個對象都專用於完成一項給定的任務。Translator 對象之所以可充當這個紐帶,是因為它封裝了要完成的全部轉換邏輯。JSP 文件與 Translator 對象通信,專用於顯示信息。這使 JSP 文件變得比較“干淨”,即 JSP 文件中幾乎沒有 Java 代碼。servlet 專門處理業務對象的持久性和屏幕之間的導航流。有了 servlet 專門處理這些任務,JSP 文件就幾乎不需要 Java 代碼了,從而使 JSP 文件變得更加“干淨”。

更詳細一點
如前所述,Translator 模式由三個基本部分組成,分別是專用的 Translator 對象、servlet 和 JSP 文件。

JSP 文件的唯一職責就是充當用戶界面類。這是可能的,因為 JSP 文件從 Translator 對象獲取預先格式化好的字符串值(稍後討論)。JSP 文件應包含盡可能少的 Java 代碼,因為負責開發 JSP 文件的網頁設計者通常對 Java 編程語言知之甚少,或者干脆就一無所知。使內嵌在 JSP 文件中的 Java 代碼盡可能達到最少,這樣就使 JSP 文件更像純 HTML 頁面。與整篇都糾纏著 Java 代碼的 JSP 文件相比,純 HTML 頁面的修改要容易得多。

Translator 對象是一種專用的類,它類似於 MVC 模式中的模型 (model) 類。Translator 對象將業務對象與 JSP 文件中的顯示域聯系起來。網頁設計者將調用 Translator 對象的一個 getter 方法在 JSP 文件中顯示動態信息。Translator 對象將返回一個預先格式化好的字符串,因此網頁設計者需要做的全部工作就是將它發送到一個輸出流中。Translator 對象能夠提供此信息,因為它在內部變量中存儲著需要顯示的全部值。這些變量是由 syncGuiToModel() 和 processForm() 方法設置的。這兩個方法專門處理屏幕和 Translator 之間的信息同步。小組中的 Java 開發人員負責構建這個 Translator 對象。

最後,servlet 專用於處理導航流和業務對象的持久性。當 servlet 接收到提交表單時,它將獲得 Translator 對象的一個實例,並使用 processForm() 方法將對表單的分析委派給 Translator 對象。在表單的分析完成以後,servlet 就會讓 Translator 對象使用 syncModelToGui() 方法將業務對象的值同步為表單中所提交的值。在成功轉換所提交的值並將它們設置到業務對象上之後,servlet 將對業務對象執行持久化,並向網頁訪問者顯示確認頁。

給我顯微鏡
下面我們仔細查看 Translator 模式的三個部分:JSP 文件、servlet 和 Translator 對象。(本文的示例遵循的是 Servlet 2.1 規范和 JSP 1.0 規范。這些示例是在 WebSphere 3.02 和 WebSphere 3.5 環境下構建和測試的。)

JSP 文件
使用 Translator 模式的典型 JSP 輸入表單如下所示:

<%@ page extends="com.ibm.igs.ispkcm.translator.JspBase"
import="com.ibm.developerworks.translatorpattern.LoanTranslator,
Java.util.Hashtable"%>
<HTML>
<%
LoanTranslator ltLoan = LoanTranslator.getInstance(request);
Hashtable htErrors = ltLoan.getErrors();
%>
<HEAD>
</HEAD>
<BODY>
<%= displayErrors(htErrors) %>
<action="/servlet/com.ibm.developerworks.translatorpattern.LoanRegistrationServlet">
<%= hightLightErrors(ltLoan.BORROWER_LAST_NAME, htErrors) %>Borrower Last Name:
<INPUT name="<%= ltLoan.BORROWER_LAST_NAME %>"
value="<%= ltLoan.getBorrowerLastName() %>">
</FORM>
</BODY>
</HTML>

在 Translator 模式中,所有表單都是 JSP 文件,而非 HTML 文件,所以輸入域的值可以是動態的。這一點很重要,因為我們網站的訪問者是實實在在的人,他們會犯輸入錯誤。比讓網站通知您它不接受您的輸入更糟糕的唯一一件事情是,讓它通知您它不接受您在某個域中輸入的內容,並要求您重新鍵入其他 20 個域,因為某個程序員正忙於檢查股市報價,沒有時間來提高輸入表單的用戶友好性。在 Translator 模式中,輸入表單的每個輸入域的值都是動態設置的,因此如果因存在輸入錯誤而需要向網站訪問者重新顯示輸入頁,他們就會看到突出顯示的錯誤,但其他輸入項仍然保留。

請記住,JSP 文件是由對 Java 代碼知之甚少或一無所知的網站設計者編寫和維護的。鑒於這個原因,我們希望 JSP 文件包含盡可能少的 Java 代碼。但我們在上一段的說明聽起來好像需要編寫大量的 Java 代碼。該 JSP 文件示例只有少量 Java 代碼,但它們主要位於表達式 (<%= x %>) 中。它們能夠使用這些眾多的功能,因為 JSP 文件從它的超類 com.ibm.igs.ispkcm.translator.JspBase 中繼承了這些代碼,並將大量代碼放在 Translator 對象中。

在此 JSP 文件中,要注意的第一點是它有一個 page 指令標記。這是因為它需要繼承一個超類,並需要導入兩個類。JSP 文件繼承了超類 com.ibm.igs.ispkcm.translator.JspBase,因為 JspBase 包括一些很好的實用函數,它使得 JSP 文件可包含更少的代碼。該 JSP 文件所用的主要實用函數是 displayErrors() 和 highLightErrors()。該 page 指令導入 LoanTranslator 和 Hashtable,因為 JSP 文件中引用了這兩個類,導入這兩個類是為了以後在此 JSP 代碼中引用這兩個類時不必使用它們完全限定的類名。以下代碼顯示了 import 語句的一個示例:

<%@ page extends="com.ibm.igs.ispkcm.translator.JspBase"
import="com.ibm.developerworks.translatorpattern.LoanTranslator,
Java.util.Hashtable"%>


JSP 文件中第一行真正的 Java 代碼獲取 Translator 對象的一個實例,然後獲取一個 Hashtable,其中存儲著屬於 Translator 對象的那個實例的錯誤。因為 JSP 文件(在編譯後)是一個 servlet,所以它是一個無狀態的服務對象。Translator 對象將在特定 JSP 文件或 servlet 的不同往返之間維護必要的狀態信息。狀態信息應僅限於網站訪問者輸入的值和要向網站訪問者顯示的任何處理錯誤。因為 Translator 對象的每個實例都與一個特定的網站訪問者相關聯,所以 JSP 文件調用 Translator 對象的 getInstance(HttpServletRequest) 方法。由於為該方法傳遞的是一個 HttpServletRequest 對象,所以該方法將能夠檢索與該網站訪問者的 HttpSession 相關的 Translator 實例。

<%
LoanTranslator ltLoan = LoanTranslator.getInstance(request);
Hashtable htErrors = ltLoan.getErrors();
%>

在此 JSP 文件示例中,網站訪問者將在表單的頂部看到所有處理錯誤。用來顯示這些錯誤消息的 HTML 是使用 <%= displayErrors(htErrors) %> 表達式輸出的。displayErrors() 方法是從 JSP 文件的超類 JspBase 中繼承而來的。有了 displayErrors(),用於顯示錯誤消息的所有邏輯都被集中在一起,從而簡化了維護工作。

下面這段代碼模板適用於表單上的每個輸入域:

<%= hightLightErrors(ltLoan.BORROWER_LAST_NAME, htErrors) %>Borrower Last Name:
<INPUT name="<%= ltLoan.BORROWER_LAST_NAME %>"
value="<%= ltLoan.getBorrowerLastName() %>">


highLightErrors() 是從 JSP 文件的超類 JspBase 中繼承而來的。如果某個輸入域有錯誤,該方法將突出顯示該輸入域的標簽。該方法接收兩個參數:String 和Hashtable。該 Hashtable 是其中存儲著從 Translator 的實例檢索而來的錯誤的 Translator。String 是正在檢查其是否有錯誤的輸入域的名稱。如果存在與該域相關的錯誤,highLightErrors() 就會返回突出顯示此輸入域的標簽的 HTML。

在此代碼模板中,須注意的重要一點是:INPUT 標記的 name 屬性是由一個表達式設置的,該表達式使用 LoanTranslator 對象的 BORROWER_LAST_NAME 常量。因為在 JSP 文件中引用此域名時使用了一個常量,所以 Translator 對象和 servlet 的調試變得更加容易。使用常量更為容易,因為無論何時開發人員更改此域的名稱,他們都會獲得一個編譯錯誤,而非運行時錯誤。查找編譯錯誤要容易得多,因為編譯器會立即指出錯誤,而運行時錯誤到調試和測試期間才能被發現。

有關此代碼模板的最後一個(也是最重要的一個)注意事項是:INPUT 標記的 value 屬性是由一個表達式設置的,該表達式使用了 Translator 的一個 getter 方法。value="<%= ltLoan.getBorrowerLastName() %> 這行代碼是此模式最重要的部分之一,因為它就是使輸入域的值缺省為網站訪問者最初輸入的值的代碼。通過將此值缺省設置為網站訪問者最初輸入的值,訪問者就能看到他最初輸入的內容,並很容易地修正他的錯誤。這節省了網站訪問者的時間,並能使其獲得更好的用戶體驗。

servlet
下面是一個典型的簡化 servlet:

public void doPost(HttpServletRequest request, HttpServletResponse response)
{
// 缺省設置是使用戶返回輸入頁。
String sRedirect = LOAN_JSP;

// 獲取 Translator 的正確實例
LoanTranslator ltTrans = LoanTranslator.getInstance(request);

// 現在有了 Translator 的一個實例
ltTrans.processForm(request);

// 獲取 Loan 的正確版本的邏輯
Loan lnTheLoan = null;
if (ltTrans.isNew () == true)
{
// 創建新 Loan
}
else
{
// 獲得現有的 loan
}
// Sync the Loan object values to values in the submitted form.
ltTrans.syncModelToGui(lnTheLoan);

// 確保未出現任何錯誤
if (ltTrans.hasErrors() == false)
{
// 提交 Loan 信息,然後將重定向設置為正確的尾隨頁
sRedirect = LOAN_CONFIRMATION;
}

// 將網站訪問者重定向為當前工資。
try {
response.sendRedirect(sRedirect);
}
catch (Exception e)
{
// 錯誤邏輯
}
}

servlet 的主要用途是控制 JSP 文件之間的導航流以及對業務對象執行持久化。此 servlet 的代碼很簡單。

servlet 執行的第一個操作就是從 HttpServletRequest 中獲取 Translator 對象的一個實例。
隨後 servlet 使用 processForm() 方法將對所提交的 HTML 表單的處理委派給 Translator。
在 Translator 分析表單之後,servlet 將確定是否需要從第二存儲中創建或檢索業務對象(示例中的 Loan)。
在 servlet 擁有業務對象的一個實例之後,servlet 就會調用 Translator 對象的 syncModelToGui()。syncModelToGui() 隨後將網站訪問者提交的全部值轉換為業務對象可識別的值。
在 Translator 完成值的同步之後,servlet 將檢查當試圖轉換網站訪問者輸入的值時,Translator 是否記錄了任何錯誤。
如果沒有記錄任何錯誤,servlet 就會將網站訪問者重定向到確認頁(通常是另一個 JSP 文件,它同樣能顯示來自 Translator 的值)。
如果記錄了錯誤,servlet 就將網站訪問者重定向到輸入表單,這樣他就可以修正錯誤。

Translator 對象
因為 Translator 對象是 JSP 文件、servlet 和業務對象之間的聯系紐帶,所以要求它是有狀態的,並且要求它在 HTTP 請求(或線程)之間維護狀態。為了符合這個標准,Translator 對象需要起到類似偽孤子 (pseudo singleton) 那樣的作用。Translator 類有五個主要部分,另外還有一個前面未曾提及的 Object Translator 類庫。

getInstance()
syncGuiToModel()
若干 getter 方法
processForm()
syncModelToGui()
Object Translators 庫

為了調用 Translator 對象的一個實例,調用程序必須調用靜態方法 getInstance(HttpServletRequest)。getInstance() 方法將確定是否應返回 Translator 的一個新實例,或者是否應從 HttpSession 中重用某個實例。該方法通過查看定制參數 action 來實現這一點。此參數是隨 HTTP 請求(例如,http://localhost/registerLoan.jsp?action=new)一起傳遞的。下面的樣例代碼段顯示了 getInstance() 的內容:

public static LoanRegistrationTranslator getInstance(HttpServletRequest request)
{
// 聲明返回值變量。
LoanRegistrationTranslator lrtRV = null;

// HttpSession 是必需的,因此在此處將它提取出來。
HttpSession session = request.getSession();

// 從 request 對象中檢索 action 參數。
String sAction = parseString(request, ACTION);

// 確定我們要返回哪種 Translator。
if (ACTION_PROCESS.equals(sAction) == true)
{
// 因為 action 參數被設置為 process,這表示我們正在處理一個現有的
// Translator,所以從 HttpSession 中將這個 translator 提取出來。
lrtRV = (LoanRegistrationTranslator)
session.getValue(HttpSessionValueKeys.LOAN_REGISTRATION_TRANSLATOR);
}
else if (sAction == null || "".equals(sAction) == true ||
ACTION_NEW.equals(sAction) == true)
{
// 由於未設置或根本未傳遞 action,所以缺省操作是創建一個新的 translator。
// 另一種可能是 action 為 "new"
lrtRV = new LoanRegistrationTranslator();
session.putValue(HttpSessionValueKeys.LOAN_REGISTRATION_TRANSLATOR, lrtRV);
}
else
{
// 由於 action 不滿足前面的任何檢查,即 action 值是位於輔助存儲器中的
// 一個現有 Loan 的 ID,所以這次創建一個 LoanRegistrationTranslator,
// 其值被預設為已保存的 loan 中的值。
lrtRV = new LoanRegistrationTranslator(sAction);
session.putValue(HttpSessionValueKeys.LOAN_REGISTRATION_TRANSLATOR, lrtRV);
}

// 返回 Translator 對象的一個實例。
return lrtRV;
}


對於要在 JSP 文件中顯示的每個業務對象值,Translator 都提供了一個 getter 方法。這些 getter 方法由 JSP 文件調用。必須在 JSP 文件中顯示的每個業務對象屬性都有一個 getter 方法。getter 方法總是返回一個 String。返回的 String 值已被預先格式化,以便直接在 JSP 文件中顯示。預先格式化 String 是為了使 JSP 文件中的 Java 代碼盡可能少。對這些值的格式化改為在 syncGuiToModel() 中進行。

Translator 使用 processForm() 清除以前顯示的錯誤,並從提交的表單中分析此信息。當 servlet 接收到一個提交的表單時,它就會將對該表單的處理委托給 Translator。在本委托期間,Translator 分析提交表單的值,並將這些值分別存儲在 String 變量中。這些存儲變量隨後通過 syncModelToGui()(由 servlet 調用)轉換為業務對象的值。

syncGuiToModel() 和 syncModelToGui() 是兩個類似的方法,顧名思義,其中一個方法按某個方向對值執行同步,而另一個方法按相反的方向執行同步。syncGuiToModel() 從業務對象的屬性中提取這些值,並使用 Object Translator 預先格式化每個屬性值。Object Translator 將值預先格式化為要在屏幕上顯示的值。隨後,它將 Translator 的對應 String 變量設置為這個值。syncModelToGui() 執行的操作相同,但方向相反。下面是從典型的 syncGuiToModel() 中摘出的一小段代碼:

DoubleTranslator dtDouble = new DoubleTranslator();
String sTemp = dtDouble.translate(loan.getInterestRate());
setInterestRate(sTemp);

為了使轉換代碼具有最大的可重用性,並為整個站點提供一種共同的轉換,syncGuiToModel() 和 syncModelToGui() 使用的都是 Object Translator 類。Object Translators 與 Translator 對象聯系得如此緊密,以致於幾乎可將它們看作是 Translator 對象的規則。Object Translator 類是一個簡單的類,它的唯一用途就是將一種數據類型轉換為格式化字符串,以及將格式化字符串轉換為它的數據類型。下面是一個很簡單的 Object Translator 類的代碼:

public class DoubleTranslator extends ObjectTranslator
{
public String translate(double doubleValue)
{
return Double.toString(doubleValue);
}

public double translate(String stringToBeTranslated) throws Exception
{
double dRV = 0.0;

try {
Double dbDouble = Double.valueOf(stringToBeTranslated);
dRV = dbDouble.doubleValue();
}
catch(Exception e)
{
Exception eTranslation =
new Exception("Please enter a numeric value like 1.0 or 1.25");
throw eTranslation;
}

return dRV;
}
}


當這個樣例 Object Translator 將 double 轉換為格式化的 String 時,它只需調用 Double.toString()。這個方法可實現更強大的功能,例如,可以添加幾行代碼,使得當格式化類似 1000.25 這樣的大 double 值時,可預格式化這個值,以便顯示為 1,000.25。

對於 DoubleTranslator.translate(String),須注意的重要一點是,它會發出一個可讀性很好的異常,而不是標准的 Java.lang.NumberFormatException 異常。JSP 文件將此異常的消息直接顯示給網站訪問者,因此,讓網站的普通訪問者很容易地理解該方法發出的異常是非常重要的。

小結
使用 Translator 模式框架的優點很多,不僅可以降低網站的成本,而且可以提高用戶的滿意度。由於不同組件都只執行專門的任務,因而網站的構建成本降低了。這些專門任務分配給生產小組,如 HTML 小組或 Java 程序員小組。因為 Translator 模式的 ObjectTranslators 在整個網站重用,所有全部格式化和分析都是以相同的方式進行的。因為每項任務都以相同的方式完成,所以網站訪問者就能夠以一致的方式查看整個網站的信息。這進一步提高了用戶的滿意度。

參考資源

下載本文所用示例的源代碼。
下面的 developerWorks 教程可幫助您進一步了解 servlet 和 JSP 文件:
Building Java HTTP servlets
Building servlets with session tracking
Introduction to JavaServer Pages technology

作者簡介
Donald Bell 是 IBM 全球服務中心的 IT 專家,他為客戶提供有關基於 Web 的技術應用指導。可以通過 [email protected] 與他聯系。


摘自:http://www.linuxaid.com


Copyright © Linux教程網 All Rights Reserved