程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 如何將裝飾模式應用到servlet request對象上

如何將裝飾模式應用到servlet request對象上

編輯:關於JAVA

本文說明了如何將裝飾模式應用到servlet request對象上。首先,提出了一個與servlet filter有關的問題,並解釋了隨之而引入的裝飾模式。然後,討論了如何在servlet環境下使用此模式,並列出了使用此模式的幾個比較有名的基於servlet的項目。最後,文章通過實現一個刪除空白符的filter例子,演示了裝飾模式在servlet中的使用。

簡介

Servlet規范中所引入的filter令人心動不已,因為它引入了一個功能強大的攔截模式。Filter是這樣一種Java對象,它能在request到達servlet的服務方法之前攔截HttpServletRequest對象,而在服務方法轉移控制後又能攔截HttpServletResponse對象。你可以使用filter來實現特定的任務,比如驗證用戶輸入,以及壓縮web內容。但你擬富有成效地使用過濾器的念頭卻被你不能改變HttpServletRequest對象的參數的現實掃了興,因為java.util.Map所包裝的HttpServletRequest對象的參數是不可改變的。這極大地縮減了filter的應用范圍。至少在一半的時間裡,你希望可以改變准備傳送給filter的對象。如果在HttpServletRequest對象到達Struts的action servlet之前,我們可以通過一個filter將用戶輸入的多余空格去掉,難道不是更美妙嗎?這樣的話,你就不必等到在Struts的action表單驗證方法中才進行這項工作了。

幸運的是,盡管你不能改變不變對象本身,但你卻可以通過使用裝飾模式來改變其狀態。

裝飾模式

在繼承中,你可以通過繼承一個父類並覆蓋你希望改變的方法來改變對象狀態。然而,如果這個對象是由程序的另一個子模塊,例如對象工廠 (這裡所說的工廠是工廠模式中的術語,下同。譯者注) 或是servlet容器所產生的,繼承就無能為力了。

裝飾模式可用來增加一個現有對象的功能,或是改變其狀態。與其使用繼承方式來擴展此類,這個模式將一個對象包裝成另外一個對象。圖1是裝飾模式的UML類圖。

圖1:裝飾模式

在圖1中,Component是一個接口,其具體實現是ConcreteComponent。要改變Component的狀態,你可以修改ConcreteComponent或是擴展它 (通過繼承或實現接口的方式,譯者注)。然而,如果ConcreteComponent來自於一個工廠,你卻無計可施。你所能做的,就是創建一個同為實現了Component接口的裝飾類。在圖1中,這個裝飾類的角色就由Decorator來扮演,在程序中通常表現為接口或抽象類。Decorator類的一個特性就是,它有一個接收Component對象的構造方法。你將擬裝飾的對象傳遞給這個構造方法。在本例中,這個對象就是從工廠獲得的ConcreteComponent對象。通過將此裝飾對象傳遞給Decorator的一個類變量,你可以訪問Decorator中的任何方法。這就使你得以改變對象的狀態了。

圖1中的Decorator類不一定是接口或抽象類。如果你的程序不是很復雜,你可以將其轉化為一個具體的Decorator類。

舉個例子,考慮這樣一個簡單的消息傳遞程序,其主要部分是Messenger接口及其實現類MessengerImpl。讓我們假設MessengerImpl對象來自於一個工廠,因此你不能改變其狀態。如果你准備增加或改變Messenger對象的功能,你可以創建一個MessengerDecorator類。圖2是此例子的類圖。

圖2:Messenger裝飾類

我們來看程序的代碼。列表1給出了Messenger接口的代碼,列表2是MessengerImpl類的代碼。

列表1:Messenger接口

public interface Messenger { public String getMessage();}

列表2:MessengerImpl類

public class MessengerImpl implements Messenger { private String message;
public MessengerImpl(String message) {  this.message = message;
} public String getMessage() {  return message;
}
}

Messenger對象由一個名為MessengerFactory的工廠創建,如列表3所示。

列表3:MessengerFactory類

public class MessengerFactory {
public static Messenger getMessenger()
{
return new MessengerImpl("secrets");
}
}

對每一個所創建的Messenger對象,此工廠通過某個未知的操作,初始化了getMessage()方法所返回的字符串。換句話說,你不能自己創建Messenger對象。

在程序中,Messenger對象的主要用途是被傳遞給一個名為Util的類中的broadcast()靜態方法。列表4是Util類的代碼。

列表4:Util類

public class Util {
public static void broadcast(Messenger messenger) {
System.out.print(messenger.getMessage());
}
// other methods here}

在你自己的類中,你可能會有這樣的代碼:

Messenger messenger = MessengerFactory.getMessenger();

Util.broadcast(messenger);

假設你希望對broadcast()方法所打印出的消息做一小改動。你擬將其轉為大寫,怎麼做?表面上看,你可以繼承Messenger,實例化其子類,並將返回的對象傳給Util.broadcast()。但是,這種做法毫無意義,因為只有工廠才知道如何初始化Messenger對象,並通過其getMessage()方法返回正確的值。

使用裝飾模式,你可以創建一個MessengerDecorator類,如列表5所示。

列表5:MessengerDecorator類

public class MessengerDecorator implements Messenger { private Messenger messenger;
public MessengerDecorator(Messenger messenger) {  this.messenger = messenger;
} public String getMessage() {  return messenger.getMessage().toUpperCase();
}
}

因為MessengerDecorator實現了Messenger,Util.broadcast()將接受一個MessengerDecorator的實例。然而,MessengerDecorator不僅僅是一個接口的實現,它還是一個MessengerImpl對象的裝飾器。正因如此,MessengerDecorator就必須有一個接收擬被裝飾的Messenger對象的構造方法。

如列表5所示,這個構造方法將參數傳給變量。你現在可以覆蓋MessengerDecorator中的getMessage()方法,以便將消息轉為大寫後再打印出來。因為你持有原來Messenger對象的引用,你可以這樣寫getMessage()方法:

public String getMessage() {
return this.messenger.getMessage().toUpperCase();
}

MessengerDecorator中的getMessage()方法返回原始消息的大寫版本。

在你的類中,就像往常一樣,你得到一個Messenger對象,並將Decorator傳給Util.broadcast()。

Messenger messenger = factory.getMessenger();

Util.broadcast(new MessengerDecorator(messenger));

你並不將原始對象傳給原先的目標,相反,你將其傳給了該對象的裝飾器。

應用裝飾模式於Servlet

以上Messenger類的例子與servlet容器所構造的ServletRequest對象是一樣的。當收到一個HTTP請求時,servlet容器就會創建ServletRequest對象及ServletResponse對象(分別是ServletRequestImpl及ServletResponseImpl的實例),並將這兩個對象傳遞給特定的servlet服務方法。現在,如果你為ServletRequest創建一個裝飾角色,並將其傳給servlet服務方法,你就應用了裝飾模式。

對ServletRequest很容易應用裝飾模式,因為servlet API已經為其提供了一個包裝類:ServletRequestWrapper。圖3是一個servlet裝飾模式的類圖。

圖3:Servlet API中的裝飾模式

圖3中的HTTP版本的類圖如圖4所示。別為過多的類搞暈了頭,只管注意虛線框中的三個類就行了:HttpServletRequest, HttpServletRequestImpl, HttpServletRequestWrapper。

圖4:Servlet API (HTTP)的裝飾模式

情況與前面所舉例子類似。你擁有一個ServletRequest的實現,而它是由servlet容器產生的。你可以使用所提供的ServletRequestWrapper來裝飾這些ServletRequest對象。

這個模式很簡單,在實際應用中可以派上用場。實際上,一些很有名的應用就使用了此模式。這些應用包括:

Struts - Struts是當前開發Java Web應用最受歡迎的基於MVC(模型-視圖-控制)模式的框架。Struts提供了相當於ServletRequest包裝類的org.apache.struts.upload.MultipartRequestWrapper類。MultipartRequestWrapper覆蓋了getParameter(),getParameterNames(),及getParameterValues()等方法來實現文件上傳。

Apache Beehive ?C 這個源於BEA的WebLogic專題小組的開源項目,構建於Struts之上,並簡化了web應用及web服務的開發。與ServletRequest包裝類一樣,org.apache.beehive.netui.pageflow.internal包中的PageFlowRequestW

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved