1. 程式人生 > >Java設計模式-責任鏈模式

Java設計模式-責任鏈模式

對象行為型模式 結束 PE 缺點 滿足 repl top 調用 規則

責任鏈模式又稱為職責鏈模式,在23種設計模式中歸類為行為型模式。行為型模式可以分為類行為型模式和對象行為型模式。

類行為型模式使用繼承關系在幾個類之間分配行為,類行為型模式主要通過多態等方式來分配父類與子類的職責。

對象行為型模式則使用對象的聚合關聯關系來分配行為,對象行為型模式主要是通過對象復合等方式來分配兩個或多個類的職責。根據“合成復用原則”,系統中要盡量使用組合關系來取代繼承關系,因此大部分行為型設計模式都屬於對象行為型設計模式。責任鏈模式就是對象行為型模式。

在責任鏈模式中,有多個對象都有機會處理請求,避免請求發送者與接收者耦合在一起。將這些對象連接成一條鏈,並且沿著這條鏈傳遞請求,直到有對象處理它為止。

典型責任鏈模式
責任鏈一般涉及到兩個角色:

抽象處理者(Handler)角色:定義出一個處理請求的接口。如果需要,接口可以定義出一個方法以設定和返回對後繼者的引用。這個角色通常由一個Java抽象類或者Java接口實現。
具體處理者(ConcreteHandler)角色:具體處理者接到請求後,可以選擇將請求處理掉,或者將請求傳給後繼者。由於具體處理者持有對後繼者的引用,因此,如果需要,具體處理者可以訪問後繼者。

典型抽象處理者Handler代碼
public abstract class Handler {

private Handler successor;

public Handler getSuccessor() {
    return successor;
}

public void setSuccessor(Handler successor) {
    this.successor = successor;
}  

public abstract void handleRequest();

}
典型具體處理者ConcreteHandler代碼
public class ConcreteHandler extends Handler {

@Override
public void handleRequest() {
    if (請求滿足條件) {
        ...//處理請求
    } else {
        getSuccessor().handleRequest();//轉發給後繼者
    }
}

}
責任鏈模式示例
UI控件事件傳遞
HelpHandler類定義了一個處理幫助請求的接口。它持有對象鏈中後繼者的引用successor,關鍵處邏輯handleHelp()方法可以被子類重寫,hasHelp()是一個條件判斷,主要用於判斷是否符合當前對象的處理條件,如果滿足則會執行handleHelp()方法。

public enum Topic {

NONE,BUTTON,DIALOG,APPLICATION

}

public abstract class HelpHandler {

private HelpHandler successor;
private Topic topic;

HelpHandler(HelpHandler successor, Topic topic) {
    this.successor = successor;
    this.topic = topic;
}

public void handleHelp() {
    if (successor != null) {
        successor.handleHelp();
    }
}

public boolean hasHelp() {
    return topic != Topic.NONE;
}

}
所有的窗口組件都是Widget的子類,Widget是HelpHandler的子類,因為所有的界面元素都可以有幫助主題。

public abstract class Widget extends HelpHandler{

public Widget(HelpHandler successor, Topic topic) {
    super(successor,topic);
}

}
Button是鏈上的第一個事件處理者, 由於Button也是一個窗口與組件,所以它是Widget的子類,在構造方法的入參中傳入的是一個Widget,而並不是一個HelpHandler類 。

public class Button extends Widget {

public Button(Widget w, Topic topic) {
    super(w, topic);
}

@Override
public void handleHelp() {
    if (hasHelp()) {
        System.out.println("Button handler");
    } else {
        super.handleHelp();
    }
}

}
Dialog類似Button的實現,只不過它的後繼者不是一個Widget組件,而是一個任意的幫助請求處理對象,在本示例中它的後繼者將是一個Application實例。

public class Dialog extends Widget {

public Dialog(HelpHandler successor, Topic topic) {
    super(successor, topic);
}

@Override
public void handleHelp() {
    if (hasHelp()) {
        System.out.println("Dialog handler");
    } else {
        super.handleHelp();
    }
}

}
在鏈的末端是一個Application實例,該Application不是一個窗口組件。當一個幫助請求傳遞到這一層時,Application可提供關於該應用的一般性信息,或者它可以提供一系列不同的幫助主題。

public class Application extends HelpHandler {

Application(HelpHandler successor, Topic topic) {
    super(successor, topic);
}

Application(Topic topic) {
    this(null, topic);
}

@Override
public void handleHelp() {
    if (hasHelp()) {
        System.out.println("Application handler");
    } else {
        //具體邏輯操作
    }
}

}
示例代碼調用如下:

Application application = new Application(Topic.APPLICATION);
Dialog dialog = new Dialog(application, Topic.DIALOG);
Button button = new Button(dialog, Topic.BUTTON);
button.handleHelp();//Button handler

Application application = new Application(Topic.APPLICATION);
Dialog dialog = new Dialog(application, Topic.DIALOG);
Button button = new Button(dialog, Topic.NONE);
button.handleHelp();//Dialog handler
該示例的處理方式類似UI的用戶事件,在Android中的事件處理也是使用的責任鏈模式。現在比較流行的網絡請求庫OkHttp,它的內部在處理網絡請求時就是使用的責任鏈模式,在J2EE開發中的Filter也是責任鏈模式,可以看出在一些框架層面上面,責任鏈模式使用相當廣泛。

仿寫Servlet中Filter
有一個字符串String msg = “:):,<script>,敏感,被就業,網絡授課”;我們希望應用以下三個規則對字符串進行過濾和諧處理:

將字符串中出現的”<>”符號替換成”[]”;
處理字符串中的敏感信息,將被就業和諧成就業;
將字符串中出現的”:):”轉換成”^V^”;
首先看一下定義的Request和Response以及Filter接口。

public class Request {

public String request;

}
public class Response {

public String response;

}
public interface Filter {

// 定義接口Filter,具體的過濾規則需要實現這個接口
void doFilter(Request request, Response response, FilterChain chain);

}
FilterChain的定義如下:

public class FilterChain implements Filter {

// 用List集合來存儲過濾規則
List<Filter> filters = new ArrayList<Filter>();
// 用於標記規則的引用順序
int index = 0;

// 往規則鏈條中添加規則
public FilterChain addFilter(Filter f) {
    filters.add(f);
    // 代碼的設計技巧:Chain鏈添加過濾規則結束後返回添加後的Chain,方便我們下面doFilter函數的操作
    return this;
}

@Override
public void doFilter(Request request, Response response, FilterChain chain) {
    // index初始化為0,filters.size()為3,不會執行return操作
    if (index == filters.size()) {
        return;
    }
    // 每添加一個過濾規則,index自增1
    Filter f = filters.get(index);
    index++;
    // 根據索引值獲取對應的規律規則對字符串進行處理
    f.doFilter(request, response, chain);
}

}
定義過濾處理邏輯。

public class HtmlFilter implements Filter {

@Override
public void doFilter(Request request, Response response, FilterChain chain) {
    request.request = request.request.replace(‘<‘, ‘[‘).replace(‘>‘, ‘]‘) +
    // 後面添加的是便於我們觀察代碼執行步驟的字符串
            "----HTMLFilter()";
    chain.doFilter(request, response, chain);
    response.response += "---HTMLFilter()";
}

}
public class FaceFilter implements Filter {

@Override
public void doFilter(Request request, Response response, FilterChain chain) {
    // 將字符串中出現的":):"轉換成"^V^";
    request.request = request.request.replace(":):", "^V^")
    // 後面添加的是便於我們觀察代碼執行步驟的字符串
            + "----FaceFilter()";
    chain.doFilter(request, response, chain);
    response.response += "---FaceFilter()";
}

}
public class SensitiveFilter implements Filter {

@Override
public void doFilter(Request request, Response response, FilterChain chain) {
    // 處理字符串中的敏感信息,將被就業和諧成就業
    request.request = request.request.replace("被就業", "就業")
            .replace("敏感", "") +
    // 後面添加的是便於我們觀察代碼執行步驟的字符串
            " ---sensitiveFilter()";
    chain.doFilter(request, response, chain);
    response.response += "---sensitiveFilter()";
}

}
示例測試代碼如下:

String msg = ":):,<script>,敏感,被就業,網絡授課";
Request request = new Request();
request.request = msg;
Response response = new Response();
response.response = "response:";
// FilterChain,過濾規則形成的攔截鏈條
FilterChain fc = new FilterChain();
// 規則鏈條添加過濾規則,采用的是鏈式調用
fc.addFilter(new HtmlFilter())
.addFilter(new SensitiveFilter())
.addFilter(new FaceFilter());
// 按照FilterChain的規則順序,依次應用過濾規則
fc.doFilter(request, response, fc);
// 打印請求信息
System.out.println(request.request);
// 打印響應信息
System.out.println(response.response);
示例輸出如下:

^V^,[script],,就業,網絡授課----HTMLFilter() ---sensitiveFilter()----FaceFilter()
response:---FaceFilter()---sensitiveFilter()---HTMLFilter()
小結
職責鏈模式的主要 優點 在於可以降低系統的耦合度,簡化對象的相互連接,同時增強給對象指派職責的靈活性,增加新的請求處理類也很方便;其主要 缺點 在於不能保證請求一定被接收,且對於比較長的職責鏈,請求的處理可能涉及到多個處理對象,系統性能將受到一定影響,而且在進行代碼調試時不太方便。

責任鏈可以分為純的和不純的責任鏈。一個純的職責鏈模式要求一個具體處理者對象只能在兩個行為中選擇一個:一個是承擔責任,另一個是把責任推給下家。不允許出現某一個具體處理者對象在承擔了一部分責任後又將責任向下傳的情況。在一個純的職責鏈模式裏面,一個請求必須被某一個處理者對象所接收;在一個不純的職責鏈模式裏面,一個請求可以最終不被任何接收端對象所接收。

Java設計模式-責任鏈模式