1. 程式人生 > >設計模式之管道和過濾器的應用

設計模式之管道和過濾器的應用

軟體設計的一個核心問題是能否使用重複的體系架構,即能否達到體系架構級的軟體重用。也就是說,能否在不同的軟體系統中,使用同一體系架構。基於這個目的,許多學者們開始研究和實踐軟體體系架構的模式問題。在<Pattern-Oriented Software Architecture (面向模式的軟體體系架構) >中首次提出了8種體系結構模式:

層(L a y e r s)、管道和過濾器(Pipes and Filters) 、黑板( B l a c k b o a r d )、代理者( B r o k e r)、模型-檢視-控制器( M o d e l - Vi e w - C o n t r o l l e r)、表示-抽象-控制(P r e s e n t a t i o n - A b s t r a c t i o n - C o n t r o l)、微核(M i c r o k e r n e l)、映像(R e f l e c t i o n)。

管道和過濾器(Pipes and Filters)體系架構模式是為處理資料流的系統提供的一種模式。它是由過濾器和管道組成的.每個處理步驟都被封裝在一個過濾器元件中,資料通過相鄰過濾器之間的管道進行傳輸。每個過濾器可以單獨修改,功能單一,並且它們之間的順序可以進行配置。下圖是管道/過濾器模式的示意圖。一個典型的管道/過濾器體系結構的例子是以Unix shell編寫的程式。Unix既提供一種符號,以連線各組成部分(Unix的程序),又提供某種程序執行時機制以實現管道。另一個著名的例子是傳統的編譯器。傳統的編譯器一直被認為是一種管道系統,在該系統中,一個階段(包括詞法分析、語法分析、語義分析和程式碼生成)的輸出是另一個階段的輸入。

4.1 Servlet Filter概述

凡是開發過J2EE的web application的人員都知道,經常需要處理以下幾種情況:

  • 訪問特定資源(Web 頁、JSP 頁、servlet)時的身份認證
  • 應用程式級的訪問資源的稽核和記錄
  • 應用程式範圍內對資源的加密訪問,它建立在定製的加密方案基礎上
  • 對被訪問資源的及時轉換, 包括從 servlet 和 JSP 的動態輸出

在servlet2.3之前這些功能處理是很難實現的,但是Java Servlet 2.3 規範新增了不少激動人心的功能,其中之一便是過濾器(Filter),其實這就是我們所說的管道和過濾器體系架構在J2EE中的應用實踐. 通過使用該模式使得Web Application開發者能夠在請求到達Web資源之前擷取請求,在處理請求之後修改應答。其結構圖如下:





一個執行過濾器的Java 類必須實現javax.servlet.Filter 介面。這一介面含有三個方法:


  • init(FilterConfig):這是容器所呼叫的初始化方法。它保證了在第一次 doFilter() 呼叫前由容器呼叫。它能獲取在 web.xml 檔案中指定的filter初始化引數。
  • doFilter(ServletRequest, ServletResponse, FilterChain):這是一個完成過濾行為的方法。它同樣是上一個過濾器呼叫的方法。引入的 FilterChain 物件提供了後續過濾器所要呼叫的資訊。
  • destroy():容器在銷燬過濾器例項前,doFilter()中的所有活動都被該例項終止後,呼叫該方法。




4.2 Filter鏈介紹


所有過濾器都服從呼叫的過濾器鏈,並通過定義明確的介面得到執行。WebApplication可以指定許多過濾器來完成相關的工作.那麼它們就組成一個過濾器鏈來完成相應的工作.其結構如下圖:





4.3 例子


4.3.1 簡單filter


在PetStore1.3.1中的就存在兩個Filter過濾器.其中一個過濾器,完成字符集的編碼的轉化,如大家經常遇到的漢字編碼問題,你只需配置為GBK即可.它從Web.xml之中讀取這些引數的配置資訊,然後進行編碼的轉化.另一個是安全校驗Fliter,它負責進行安全檢查哪些頁面可以進行,哪些不可.它們組成一個Filter鏈,結構圖和實現程式碼如下(完整程式碼參見附件):






容器通過 Web 應用程式中的配置描述符 web.xml 檔案解析過濾器配置資訊。有兩個新的標記與過濾器相關:<filter> 和 <filter-mapping>。<filter> 標記是一個過濾器定義,它必定有一個 <filter- name> 和 <filter-class> 子元素。<filter-name> 子元素給出了一個與過濾器例項相關的名字。<filter-class> 指定了由容器載入的實現類。您能隨意地包含一個 <init-param> 子元素為過濾器例項提供初始化引數。<filter-mapping> 標記代表了一個過濾器的對映,指定了過濾器會對其產生作用的 URL 的子集。



4.3.2 複雜的filter


上面是petstore的例子,演示了通過Fliter修改字元編碼和安全認證的功能.下面提供一個示例演示通過修改返回資料(通過過濾器把response的字串變成大寫).



該示例使用HttpServletResponseWrapper技術,它是對HttpServletResponse的包裝,其實就是裝飾(decorate)設計模式的應用.這個例子能夠工作的關鍵是UCaseResponse和UCaseWriter類,它實現了對每個要輸出的字元都轉成了大寫後再寫入實際的輸出流的功能。


4.4 體系架構的實現


實現一個管道和過濾器一般要注意以下幾個方面:


  • 把系統任務分成一系列處理階段。
    根據管道和過濾器的設計方案,必須把系統處理的任務分割成相應獨立的任務,如日誌,資料轉化,安全認證等.這樣每個階段僅依賴其前一階段的輸出。通過資料流將所有階段相連起來。並且你可以進行替換每個步驟,或者可以調整它們之間的順序,以產生新的結果.
    如petstore中的編碼轉化Filter和安全Filter,分成兩個獨立的處理階段.

  • 定義沿每個管道傳輸的資料格式。
    我們知道每個過濾器,定義一個統一格式以獲得最大的靈活性,因為它使過濾器的重組變得容易。如:每個過濾器的方法是doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)它們的引數是必須相同的.

  • 決定如何實現每個管道連線
    Filter過濾器的連線是推得方式來實現的.前一個過濾器主動的呼叫filterChain.doFilter(request, response);來實現轉向下一個過濾器.

  • 設計和實現過濾器
    設計每個Filter具有獨立的功能,如編碼轉化,安全校驗,等功能.並且每個Fliter都應該在實現javax.servlet.Filter介面.

  • 建立處理流水線
    過濾器的部署是在Web.xml中進行配置,描述過濾器的實現類以及它們的map關係,來確定它們的順序.

在使用管道和過濾器模式時,一般會使用以下的GOF設計模式.

6.1 職責鏈模式(Chain Of Responsibility)

職責鏈設計模式的意圖就是使多個物件都有機會處理請求,從而避免請求的傳送者和接收者之間的耦合關係。將這些物件連成一條鏈,並沿著這條鏈傳遞該請求,直到有一個物件處理它為止。其實管道和過濾器模式就是職責鏈模式的抽象,把它應用到軟體體系架構中.

6.2 命令模式(Command)

命令模式的意圖將一個請求封裝為一個物件,從而使你可用不同的請求對客戶進行引數化;對請求排隊或記錄請求日誌,以及支援可撤消的操作。在管道和過濾器模式中每個過濾器一般使用命令模式,把請求封裝成一個命令進行處理.

6.3 裝飾模式(Decorator)

裝飾模式的意圖就是動態地給一個物件新增一些額外的職責。就增加功能來說,Decorator模式相比生成子類更為靈活。

在管道和過濾器模式中,在每個過濾器中經常需要對請求進行動態的增加功能,或者修改請求的內容,這時一般會使用裝飾模式.如Servlet filter的javax.servlet.http.HttpServletRequestWrapper, javax.servlet.http.HttpServletResponseWrapper就是裝飾模式的應用.