天天看點

設計模式--責任鍊(Chain of Responsibility)模式1. 責任鍊(Chain of Responsibility)模式2. 代碼示例3. 總結參考

1. 責任鍊(Chain of Responsibility)模式

1.1. 概念

在閻宏博士的《JAVA與模式》一書中開頭是這樣描述責任鍊(Chain of Responsibility)模式的:

責任鍊模式是一種對象的行為模式。在責任鍊模式裡,很多對象由每一個對象對其下家的引用而連接配接起來形成一條鍊。請求在這個鍊上傳遞,直到鍊上的某一個對象決定處理此請求。

發出這個請求的用戶端并不知道鍊上的哪一個對象最終處理這個請求,這使得系統可以在不影響用戶端的情況下動态地重新組織和配置設定責任。

責任鍊在生活中也有應用,如擊鼓傳花。

責任鍊可能是一條直線、一個環鍊或者一個樹結構的一部分

1.2. 責任鍊模式的結構

下面使用了一個責任鍊模式的最簡單的實作。

設計模式--責任鍊(Chain of Responsibility)模式1. 責任鍊(Chain of Responsibility)模式2. 代碼示例3. 總結參考

責任鍊模式涉及到的角色如下所示:

  • 抽象處理者(Handler)角色:定義出一個處理請求的接口。如果需要,接口可以定義用于設定和傳回對下家的引用的方法。這個角色通常由一個Java抽象類或者Java接口實作。上圖中Handler類的聚合關系給出了具體子類對下家的引用,抽象方法handleRequest()規範了子類處理請求的操作。
  • 具體處理者(ConcreteHandler)角色:具體處理者接到請求後,可以選擇将請求處理掉,或者将請求傳給下家。由于具體處理者持有對下家的引用,是以,如果需要,具體處理者可以通路下家。

1.3. 純的與不純的責任鍊模式

責任鍊模式根據責任鍊上每一個處理者承擔的責任,又分為純與不純。

1.3.1 純的責任鍊模式

純的責任鍊模式要求: 一個具體的處理者對象隻能在兩個行為中選擇一個:承擔責任、把責任推給下家。不允許出現某一個具體處理者對象在承擔了一部分責任後又把責任向下傳的情況。

在一個純的責任鍊模式裡面,一個請求必須被某一個處理者對象所接收;在一個不純的責任鍊模式裡面,一個請求可以最終不被任何接收端對象所接收。

純的責任鍊模式的實際例子很難找到,一般看到的例子均是不純的責任鍊模式的實作。

1.3.2. 不純的責任鍊模式

不純的責任鍊模式允許某個請求被一個具體處理者部分處理後再向下傳遞,或者一個具體處理者處理完某請求後其後繼處理者可以繼續處理該請求,而且一個請求可以最終不被任何處理者對象所接收

2. 代碼示例

2.1. 簡單示例

示例實作的是一個純的責任鍊模式

具體處理者根據條件判斷是否是自己責任,如果是則承擔全部責任,否則傳遞給責任鍊上的下一個處理者。

import lombok.Data;

class ChainClient {

    public static void main(String[] args) {
        AbstractHandler handlerA = new ConcreteHandlerA();
        AbstractHandler handlerB = new ConcreteHandlerB();
        AbstractHandler handlerZ = new ConcreteHandlerZ();
        // 如A處理不掉轉交給B
        handlerA.setSuccessor(handlerB);
        handlerB.setSuccessor(handlerZ);
        handlerA.handleRequest("Z");
    }

}

@Data
abstract class AbstractHandler {

    private AbstractHandler successor;

    public abstract void handleRequest(String condition);
}

class ConcreteHandlerA extends AbstractHandler {
    @Override
    public void handleRequest(String condition) {
        if (condition.equals("A")) {
            System.out.println("ConcreteHandlerA處理");
        } else {
            if(getSuccessor() != null) {
                System.out.println("ConcreteHandlerA不處理,由其他的Handler處理");
                getSuccessor().handleRequest(condition);
            }
        }
    }
}

class ConcreteHandlerB extends AbstractHandler {
    @Override
    public void handleRequest(String condition) {
        if (condition.equals("B")) {
            System.out.println("ConcreteHandlerB處理");
        } else {
            if(getSuccessor() != null) {
                System.out.println("ConcreteHandlerB不處理,由其他的Handler處理");
                getSuccessor().handleRequest(condition);
            }
        }
    }
}

class ConcreteHandlerZ extends AbstractHandler {
    @Override
    public void handleRequest(String condition) {
        //一般是最後一個處理者
        System.out.println("ConcreteHandlerZ處理");
    }
}
           

2.2. 請假流程

import lombok.*;

class ResponsibilityTest {
    public static void main(String[] args) {
        AbstractLeaveHandler directLeaderLeaveHandler = new DirectLeaderLeaveHandler("組長");
        DeptManagerLeaveHandler deptManagerLeaveHandler = new DeptManagerLeaveHandler("部門經理");
        GManagerLeaveHandler gManagerLeaveHandler = new GManagerLeaveHandler("總監");

        directLeaderLeaveHandler.setNextHandler(deptManagerLeaveHandler);
        deptManagerLeaveHandler.setNextHandler(gManagerLeaveHandler);

        LeaveRequest request = LeaveRequest.builder().leaveDays(2).name("小明").build();
        directLeaderLeaveHandler.handlerRequest(request);
    }
}

@Setter
@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
class LeaveRequest {
    /**天數*/
    private int leaveDays;
    /**姓名*/
    private String name;
}

@Data
class AbstractLeaveHandler {
    /**直接主管審批處理的請假天數*/
    protected int MIN = 1;
    /**部門經理處理的請假天數*/
    protected int MIDDLE = 3;
    /**總經理處理的請假天數*/
    protected int MAX = 30;

    /**下一個處理節點(即更進階别的上司)*/
    private AbstractLeaveHandler nextHandler;

    /**上司名稱*/
    protected String handlerName;

    /**處理請假的請求,子類實作*/
    protected void handlerRequest(LeaveRequest request){
    }
}

class DirectLeaderLeaveHandler extends AbstractLeaveHandler{
    public DirectLeaderLeaveHandler(String name) {
        this.handlerName = name;
    }

    @Override
    protected void handlerRequest(LeaveRequest request) {
        if(request.getLeaveDays() <= this.MIN){
            System.out.println("直接主管:" + handlerName + ",已經處理;流程結束。");
            return;
        }

        if(null != getNextHandler()){
            System.out.println("直接主管:" + handlerName + "請假确認");
            getNextHandler().handlerRequest(request);
        }else{
            System.out.println("審批拒絕!");
        }
    }
}
class DeptManagerLeaveHandler extends AbstractLeaveHandler {

    public DeptManagerLeaveHandler(String name) {
        this.handlerName = name;
    }

    @Override
    protected void handlerRequest(LeaveRequest request) {
        if(request.getLeaveDays() >this.MIN && request.getLeaveDays() <= this.MIDDLE){
            System.out.println("部門經理:" + handlerName + ",已經處理;流程結束。");
            return;
        }

        if(null != getNextHandler()){
            System.out.println("部門經理:" + handlerName + "請假确認");
            getNextHandler().handlerRequest(request);
        }else{
            System.out.println("審批拒絕!");
        }
    }
}

class GManagerLeaveHandler extends AbstractLeaveHandler {
    public GManagerLeaveHandler(String name) {
        this.handlerName = name;
    }

    @Override
    protected void handlerRequest(LeaveRequest request) {
        if(request.getLeaveDays() > this.MIDDLE && request.getLeaveDays() <= this.MAX){
            System.out.println("總經理:" + handlerName + ",已經處理;流程結束。");
            return;
        }

        if(null != getNextHandler()){
            getNextHandler().handlerRequest(request);
        }else{
            System.out.println("審批拒絕!");
        }
    }
}
           

2.3 okhttp請求攔截器的實作

詳見可參見: http架構–OkHttp 4 架構與源碼分析

OkHttp中使用者可傳入的interceptor分為兩類:

  • 全局interceptor,該類interceptor在請求開始之前最早被調用
  • 為非網頁請求的networkInterceptor,這類interceptor隻有在非網頁請求中會被調用,并且是在組裝完成請求之後,真正發起請求之前被調用(這塊具體可以參看RealCall#getResponseWithInterceptorChain()方法)

截圖中的incerceptors就是責任鍊,責任鍊上每一個攔截器的作用見表格。實作的是一個不純的責任鍊,責任鍊上的每一個處理者都針對http請求進行處理,但不同的處理者實作的責任功能不同。

設計模式--責任鍊(Chain of Responsibility)模式1. 責任鍊(Chain of Responsibility)模式2. 代碼示例3. 總結參考
攔截器 責任
使用者自定義的攔截器 使用者實作的一些自定義攔截功能,如記錄日志
retryAndFollowUpInterceptor 重試和重定向攔截器,主要負責網絡失敗重連
BridgeInterceptor 主要是橋接應用層和網絡層: 添加必要的請求頭資訊【encoding,cookie,userAgent等】、gzip處理等
CacheInterceptor 緩存攔截器,主要負責攔截緩存
ConnectInterceptor 網絡連接配接攔截器,主要負責正式開啟http請求
CallServerInterceptor 負責發送網絡請求和讀取網絡響應
設計模式--責任鍊(Chain of Responsibility)模式1. 責任鍊(Chain of Responsibility)模式2. 代碼示例3. 總結參考

2.4. 其它示例

Spring攔截器鍊、servlet過濾器鍊等很多開源軟體都采用了責任鍊設計模式

3. 總結

責任鍊模式通過建立一條鍊來組織請求的處理者,請求将沿着鍊進行傳遞,請求發送者無須知道請求在何時、何處以及如何被處理,實作了請求發送者與處理者的解耦。在軟體開發中,如果遇到有多個對象可以處理同一請求時可以應用責任鍊模式,例如在Web應用開發中建立一個過濾器(Filter)鍊來對請求資料進行過濾,在工作流系統中實作公文的分級審批等等,使用職責鍊模式可以較好地解決此類問題。

3.1. 優點

  • 降低耦合度,分離了請求與處理,無須知道是哪個對象處理其請求
  • 簡化對象的互相連接配接,僅保持一個指向後者的引用,而不需保持所有候選接受者的引用
  • 擴充容易,新增具體請求處理者,隻需要在用戶端重建立鍊即可,無需破壞原代碼

3.2. 缺點

  • 如果請求沒有明确的接收者,那麼就不能保證它一定會被處理,該請求可能一直到鍊的末端都得不到處理;一個請求也可能因責任鍊沒有被正确配置而得不到處理
  • 較長的責任鍊,請求的處理可能涉及到多個處理對象,系統性能将受到一定影響,而且在進行代碼調試時不太友善。
  • 如果建鍊不當,可能會造成循環調用,将導緻系統陷入死循環。

參考

《JAVA與模式》之責任鍊模式

繼續閱讀