瞭解23種設計模式之職責鏈模式

什麼是職責鏈模式

在現實生活中,常常會出現這樣的事例: 一個請求多個對象可以處理,但每個對象的處理條件和權限不同。例如,公司員工請假,可批假的領導只有部分負責人,經理,總經理等,但每個領導能批准的天數不同,員工必須根據自己要請假的天數去找不同的領導簽名,也就是說員工必須記住每個領導的姓名、電話和地址等信息,這增加了難度。這樣的例子還有很多。

在計算機軟硬件中也有相關例子,如總線網中數據報傳送,每臺計算機根據目標地址是否同自己的地址相同來決定是否接收;還有異常處理中,處理程序根據異常的類型決定自己是否處理該異常;還有 Struts2 的攔截器JSPServletFilter等,所有這些,如果用責任鏈模式都能很好解決

責任鏈模式(Chain of Responsibility):爲了避免請求發送者與多個請求處理者耦合在一起,將所有請求的處理者通過前一對象記住其下一個對象的引用而連成一條鏈;當有請求發生時,可將請求沿着這條鏈傳遞,直到有對象處理它爲止。

注意:責任鏈模式也叫職責鏈模式。

在責任鏈模式(Chain of Responsibility)中,客戶只需要將請求發送到責任鏈上即可,無須關心請求的處理細節和請求的傳遞過程,所以責任鏈將請求的發送者和請求的處理者解耦了。

責任鏈模式是一種對象行爲型模式,其主要優點如下。
1,降低了對象之間的耦合度。該模式使得一個對象無須知道到底是哪一個對象處理其請求以及鏈的結構,發送者和接收者也無須擁有對方的明確信息。
2,增強了系統的可擴展性。可以根據需要增加新的請求處理類,滿足開閉原則。
3,增強了給對象指派職責的靈活性。當工作流程發生變化,可以動態地改變鏈內的成員或者調動它們的次序,也可動態地新增或者刪除責任。
4,責任鏈簡化了對象之間的連接。每個對象只需保持一個指向其後繼者的引用,不需保持其他所有處理者的引用,這避免了使用衆多的 if 或者 if···else 語句。,5,責任分擔。每個類只需要處理自己該處理的工作,不該處理的傳遞給下一個對象完成,明確各類的責任範圍,符合類的單一職責原則。

其主要缺點如下。
1,不能保證每個請求一定被處理。由於一個請求沒有明確的接收者,所以不能保證它一定會被處理,該請求可能一直傳到鏈的末端都得不到處理。
2,對比較長的職責鏈,請求的處理可能涉及多個處理對象,系統性能將受到一定影響。
3,職責鏈建立的合理性要靠客戶端來保證,增加了客戶端的複雜性,可能會由於職責鏈的錯誤設置而導致系統出錯,如可能會造成循環調用。

職責鏈模式結構

職責鏈模式主要包含以下角色。
1,抽象處理者(Handler)角色:定義一個處理請求的接口,包含抽象處理方法和一個後繼連接。
2,具體處理者(Concrete Handler)角色:實現抽象處理者的處理方法,判斷能否處理本次請求,如果可以處理請求則處理,否則將該請求轉給它的後繼者。
3,客戶類(Client)角色:創建處理鏈,並向鏈頭的具體處理者對象提交請求,它不關心處理細節和請求的傳遞過程。

結構圖如下:
結構圖

職責鏈模式實現

爲了更好的理解模式的意義。我們先看一個例子: 我們申請請假加薪的流程,不用職責鏈模式實現

/**
 * @ClassName Request
 * @Description 申請
 * @Version 1.0
 **/
public class Request {

    //申請類別
    private  String requestTyep;

    //申請內容
    private String RequestContext;

    //申請數量
    private int number;

    public Request(String requestTyep, String requestContext, int number) {
        this.requestTyep = requestTyep;
        RequestContext = requestContext;
        this.number = number;
    }

    public String getRequestTyep() {
        return requestTyep;
    }

    public void setRequestTyep(String requestTyep) {
        this.requestTyep = requestTyep;
    }

    public String getRequestContext() {
        return RequestContext;
    }

    public void setRequestContext(String requestContext) {
        RequestContext = requestContext;
    }

    public int getNumber() {
        return number;
    }

    public void setNumber(int number) {
        this.number = number;
    }
}
/**
 * @ClassName Manager
 * @Description TODO
 * @Version 1.0
 **/
public class Manager {

    protected String name;

    public Manager(String name) {
        this.name = name;
    }

    /**
     * @Description: 得到結果
     * @param: [managerLevel, request]
     * @return: void
     * @Author: wuchao
     * @Date: 2020/6/30 21:29
     **/
    public void  getResult(String managerLevel, Request request) {
        //判斷職位
        if(managerLevel.equals("經理")){
            //判斷事件類型:請假 ,請假天數。
            if (request.getRequestTyep().equals("請假") && request.getNumber()<=2){
                System.out.println("事由:【"+request.getRequestTyep()+"】"+"事由內容:【"+request.getRequestContext()+"】" + "天數:【"+request.getNumber()+"】");
                System.out.println(name+managerLevel+"說被批准");
            }else {
                System.out.println("事由:【"+request.getRequestTyep()+"】"+"事由內容:【"+request.getRequestContext()+"】" + "天數:【"+request.getNumber()+"】");
                System.out.println(name+managerLevel+"說我無權批准");
            }
        }else if(managerLevel.equals("總監")){
             //判斷事件類型:請假 ,請假天數。
            if (request.getRequestTyep().equals("請假") && request.getNumber()<=5){
                System.out.println("事由:【"+request.getRequestTyep()+"】"+"事由內容:【"+request.getRequestContext()+"】" + "天數:【"+request.getNumber()+"】");
                System.out.println(name+managerLevel+"說被批准");
            }else {
                System.out.println("事由:【"+request.getRequestTyep()+"】"+"事由內容:【"+request.getRequestContext()+"】" + "天數:【"+request.getNumber()+"】");
                System.out.println(name+managerLevel+"說我無權批准");
            }
        }else if(managerLevel.equals("總經理")){
            //判斷事件類型:請假,加薪 ,請假天數。
            if (request.getRequestTyep().equals("請假") ){
                System.out.println("事由:【"+request.getRequestTyep()+"】"+"事由內容:【"+request.getRequestContext()+"】" + "天數:【"+request.getNumber()+"】");
                System.out.println(name+managerLevel+"說被批准");
            }else if(request.getRequestTyep().equals("加薪")&& request.getNumber()<=1000){
                System.out.println("事由:【"+request.getRequestTyep()+"】"+"事由內容:【"+request.getRequestContext()+"】" + "加薪:【"+request.getNumber()+"】");
                System.out.println(name+managerLevel+"說被批准");
            }else if(request.getRequestTyep().equals("加薪")&& request.getNumber()>2000){
                System.out.println(name+managerLevel+"說公司還需要考察考察你!!!");
                System.out.println(name+managerLevel+"說不批准");
            }
        }
    }
}

public class MainClass {
    public static void main(String[] args) {

        //請假, 天數3天
        //Request request = new Request("請假","生病了,感冒",3);
        //Request request = new Request("加薪","升職加薪",1000);
        Request request = new Request("加薪","升職加薪",3000);

        //管理者
        String name = "老王";
        Manager manager = new Manager(name);

        //String managerLevel = "經理";
        //String managerLevel = "總監";
        String managerLevel = "總經理";
        manager.getResult(managerLevel,request);


    }
}

執行結果
這樣實現來看, 類有太多的責任,這違背了單一職責原則,增加新的管理類別,需要修改這個類,違背了開閉原則。

職責鏈模式實現

public abstract class Handler {

    protected  Handler handler;

    /**
     * 處理請求
     */
    public abstract void handleRequest(int request);

    /**
     * 設置繼任者
     * @param handler
     */
    public void setHandler(Handler handler) {
        this.handler = handler;
    }
}

/**
 * @ClassName ConcreteHandler1
 * @Description 具體的處理類,處理它所負責的請求,可訪問它的後繼任者,如果可處理該請求,就處理之,否則就將該請求轉發給它的繼任者
 * @Version 1.0
 **/
public class ConcreteHandler1 extends Handler{

    @Override
    public void handleRequest(int request) {
        if(request>=0 && request<10){
            System.out.println("具體處理者1負責處理該請求");
        }else if(handler !=null){
            //轉發給它的繼任者,也就是 ConcreteHandler2 
            handler.handleRequest(request);
        }else{
             System.out.println("沒有人處理請求");
        }

    }
}
/**
 * @ClassName ConcreteHandler2
 * @Description TODO
 * @Version 1.0
 **/
public class ConcreteHandler2 extends Handler{

    @Override
    public void handleRequest(int request) {
        if(request>=20 && request<30){
            System.out.println("具體處理者2負責處理該請求");
        }else if(handler !=null){
		
            handler.handleRequest(request);
        }else{
            System.out.println("沒有人處理請求");
        }

    }
}
public class MainClass {
    public static void main(String[] args) {
        Handler handler = new ConcreteHandler1();
        Handler handler2 = new ConcreteHandler2();
        //設置職責鏈上家與下家
        handler.setHandler(handler2);

        int[] result = {2,5,12,22,18,27,20};

        for (int i = 0; i < result.length; ++i) {
            handler.handleRequest(result[i]);
        }
    }
}

在這裏插入圖片描述

當客戶提交一個請求時,請求是沿鏈傳遞直至有一個ConcreteHandler 對象負責處理它的。 這樣做的好處是不是說請求者不用管哪個對象來處理,反正該請求會被處理就對了?是的,這就使得接收者和發送者都沒有對方的明確信息,且鏈中的對象自己也並不知道鏈的結構。結果是職責鏈可簡化對象的相互連接,它們僅需保持一個指向其後繼者的引用,這樣也就大大降低了耦合度。

模式的應用場景

前邊已經講述了關於責任鏈模式的結構與特點,下面介紹其應用場景,責任鏈模式通常在以下幾種情況使用。
1,有多個對象可以處理一個請求,哪個對象處理該請求由運行時刻自動確定。
2,可動態指定一組對象處理請求,或添加新的處理者。
3,在不明確指定請求處理者的情況下,向多個處理者中的一個提交請求。

模式的擴展

職責鏈模式存在以下兩種情況。
1,純的職責鏈模式:一個請求必須被某一個處理者對象所接收,且一個具體處理者對某個請求的處理只能採用以下兩種行爲之一:自己處理(承擔責任);把責任推給下家處理。
2,不純的職責鏈模式:允許出現某一個具體處理者對象在承擔了請求的一部分責任後又將剩餘的責任傳給下家的情況,且一個請求可以最終不被任何接收端對象所接收。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章