責任鏈設計模式(過濾器/攔截器)
責任鏈設計模式(Chain of Responsibility)的應用有:Java Web中的過濾器鏈、springmvc中的攔截器鏈,Struts2中的攔截器棧等等。
先看如下一個問題:
給定一個字串“被就業了:),敏感資訊,<script>”,對其中的HTML標記和敏感詞進行過濾或替換。本文主要以該問題設計方法的演變來講解責任鏈設計模式。
第一種設計:沒有任何設計模式
設計了一個MsgProcessor類,完成字串處理的主要工作。MainTest類是本設計中的測試類。
package zmx.designmode.test.responsibilitychain; public class MainTest { public static void main(String[] args) { //需要被過濾的語句 String msg = "被就業了:),敏感資訊,<script>"; //例項化處理類 MsgProcessor mp = new MsgProcessor(msg); String r = mp.process(); System.out.println(r); } }
package zmx.designmode.test.responsibilitychain; public class MsgProcessor { private String msg; public MsgProcessor(String msg) { this.msg = msg; } public String process() { String r = msg; // 過濾msg中的HTML標記 r = r.replaceAll("<.*>", ""); // 過濾敏感詞 r = r.replace("敏感資訊", "").replace("被就業", "就業"); return r; } }
測試結果:
就業了:)
在第一種設計中,對字串的所有處理都放在MsgProcessor類中,擴充套件性極差。如果要過濾字串中的笑臉(將”:)”替換成”^_^”),則需要改動MSgProcessor中的process方法。我們看第二種設計。
第二種設計:增加一個Filter介面
1、filter介面:
package zmx.designmode.test.responsibilitychain;
public interface Filter {
public String doFilter(String str);
}
2、定義實現幾種實現:
package zmx.designmode.test.responsibilitychain; public class HtmlFilter implements Filter { @Override public String doFilter(String str) { String r = str; //過濾msg中的HTML標記 r = r.replaceAll("<.*>", ""); return r; } }
package zmx.designmode.test.responsibilitychain;
public class SensitiveFilter implements Filter {
@Override
public String doFilter(String str) {
String r = str;
//過濾str中的敏感資訊
r = r.replace("敏感資訊", "").replace("被就業", "就業");
return r;
}
}
3、處理類
package zmx.designmode.test.responsibilitychain;
public class MsgFilterProcessor {
private String msg;
private Filter[] filters= new Filter[]{new HtmlFilter(),new SensitiveFilter()};
public MsgFilterProcessor(String msg) {
this.msg = msg;
}
public String process(){
String r = msg;
for(Filter f : filters){
r = f.doFilter(r);
}
return r;
}
}
4、測試類
package zmx.designmode.test.responsibilitychain;
public class MainTest {
public static void main(String[] args) {
//需要被過濾的語句
String msg = "被就業了:),敏感資訊,<script>";
//例項化處理類
//MsgProcessor mp = new MsgProcessor(msg);
MsgFilterProcessor mp = new MsgFilterProcessor(msg);
String r = mp.process();
System.out.println(r);
}
}
測試結果:
就業了:)
此時,如果需要過濾字串中的笑臉,只需要建立一個類FaceFilter實現Filter介面,並在MsgProcessor類中的filters欄位中登記即可。
第三種設計:責任鏈模式(FilterChain)
定義:將一個事件處理流程分派到一組執行物件上去,這一組執行物件形成一個鏈式結構,事件處理請求在這一組執行物件上進行傳遞。責任鏈模式的主要參與角色:
① 事件處理請求物件(Request)
② 執行物件(Handler)
1、FilterChain
package zmx.designmode.test.responsibilitychain;
import java.util.ArrayList;
import java.util.List;
public class FilterChain implements Filter {
public List<Filter> filters = new ArrayList<Filter>();
public FilterChain addFilter(Filter f){
filters.add(f);
return this;
}
@Override
public String doFilter(String str) {
String r = str;
for(Filter f : filters){
r = f.doFilter(r);
}
return r;
}
}
2、SimleFilter
package zmx.designmode.test.responsibilitychain;
public class SmileFilter implements Filter {
@Override
public String doFilter(String str) {
String r = str;
//過濾msg中的笑臉標記
r = r.replace(":)", "^_^");
return r;
}
}
3、MsgFilterChainProcessor
package zmx.designmode.test.responsibilitychain;
public class MsgFilterChainProcessor {
private String msg;
private FilterChain chain = new FilterChain();
public MsgFilterChainProcessor(String msg, FilterChain filterChain){
this.msg = msg;
this.chain = filterChain;
}
public String process(){
return chain.doFilter(msg);
}
}
4、MainTest
package zmx.designmode.test.responsibilitychain;
public class MainTest {
public static void main(String[] args) {
//需要被過濾的語句
String msg = "被就業了:),敏感資訊,<script>";
//例項化處理類
//MsgProcessor mp = new MsgProcessor(msg);
//MsgFilterProcessor mp = new MsgFilterProcessor(msg);
//搞一個過過濾鏈
FilterChain chain = new FilterChain();
chain.addFilter(new HtmlFilter())
.addFilter(new SensitiveFilter())
.addFilter(new SmileFilter());
//例項化處理類
MsgFilterChainProcessor mp = new MsgFilterChainProcessor(msg,chain);
String r = mp.process();
System.out.println(r);
}
}
測試結果:
就業了^_^
責任鏈模式加強版
上面的實現的過濾鏈可以用下圖a)表示出來,整個過程只對msg過濾了一次。而JavaWeb中的過濾器鏈和Struts2中的攔截器棧執行的過程可以形象的表示為圖b,很重要)。
下面用程式模擬JavaWeb中的過濾器,實現類似於對Request和Response的過濾。主要涉及的類如下所示:
public interface Filter {
void doFilter(Request req,Response resp,FilterChain chain);
}
public class HtmlFilter implements Filter {
public void doFilter(Request req, Response resp, FilterChain chain) {
//過濾req.reqStr中的HTML標記
req.reqStr = req.reqStr.replace("<", "<").replace(">", ">");
req.reqStr += "---HtmlFilter()---";
chain.doFilter(req, resp);
resp.respStr += "---HtmlFilter()---";
}
}
public class SensitiveFilter implements Filter {
public void doFilter(Request req, Response resp, FilterChain chain) {
// 過濾req.reqStr中的敏感詞
req.reqStr = req.reqStr.replace("敏感", "").replace("被就業", "就業");
req.reqStr += "===SensitiveFilter";
chain.doFilter(req, resp);
resp.respStr += "===SensitiveFilter";
}
}
public class FilterChain{
private List<Filter> filters = new ArrayList<Filter>();
//呼叫鏈上的過濾器時,記錄過濾器的位置用
private int index = 0;
public FilterChain addFilter(Filter f){
filters.add(f);
return this;
}
public void doFilter(Request req, Response resp) {
if(index == filters.size()) return;
//得到當前過濾器
Filter f = filters.get(index);
index++;
f.doFilter(req, resp, this);
}
}
public class Request {
//在Request中只保持了一個reqStr欄位記錄對Request的操作
//為了方便模擬,沒有將reqStr設定為private
String reqStr;
}
public class Response {
//在Response中只保持了一個respStr欄位記錄對Response的操作
//為了方便模擬,沒有將respStr設定為private
String respStr;
}
package org.flyne.fiter;
public class MainClass {
public static void main(String[] args) {
// 需要被過濾的語句
String msg = "被就業了:),敏感資訊,<script>";
//建立Request、Response物件
Request req = new Request();
Response resp = new Response();
req.reqStr = msg;
resp.respStr = "response";
//搞一個過濾鏈,鏈上有兩個過濾器
FilterChain chain = new FilterChain();
chain.addFilter(new HtmlFilter())
.addFilter(new SensitiveFilter());
//開始過濾
chain.doFilter(req, resp);
System.out.println(req.reqStr);
System.out.println(resp.respStr);
}
}