首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 程式設計入門

Java 設計模式:責任鏈模式深度解析

     編輯:Java 程式設計入門

模式定義

責任鏈模式將請求的發送者與接收者解耦,讓多個物件都有機會處理請求,將這些物件串成一條鏈,沿著鏈傳遞請求,直到有物件處理為止。

核心角色

  • Handler (抽象處理者): 定義處理請求的介面,持有下一個處理者的參考
  • ConcreteHandler (具體處理者): 處理自己負責的請求,否則傳給下一個
  • Client (客戶端): 建立鏈並發起請求

經典實作:遊戲技能巨集系統

場景:一鍵巨集綁定多個技能,按順序嘗試施放,技能冷卻中則跳過嘗試下一個。

1. 技能介面


public interface ISkill {
    void castSkill();
}

2. 具體技能


// 無冷卻技能
public class IceArrow implements ISkill {
    @Override
    public void castSkill() {
        System.out.println("施放──》寒冰箭");
    }
}

// 有冷卻技能(2分鐘)
public class IceBloodFast implements ISkill {
    private long lastCastTime = 0;
    private static final long COOLDOWN = 2 * 60 * 1000; // 2分鐘
    
    @Override
    public void castSkill() {
        long now = System.currentTimeMillis();
        if (now - lastCastTime < COOLDOWN) {
            System.out.println("冰血冷脈:冷卻中... (" + (COOLDOWN - (now - lastCastTime)) / 1000 + "秒)");
            return; // 跳過,讓鏈繼續
        }
        lastCastTime = now;
        System.out.println("施放──》冰血冷脈");
    }
}

3. 巨集類別(鏈管理者)


import java.util.ArrayList;
import java.util.List;

public class Macro {
    private List skills = new ArrayList<>();
    
    /** 綁定技能(加入鏈尾) */
    public void bindSkill(ISkill skill) {
        skills.add(skill);
    }
    
    /** 移除技能 */
    public void unbindSkill(ISkill skill) {
        skills.remove(skill);
    }
    
    /** 執行巨集:按順序嘗試施放每個技能 */
    public void castSkill() {
        for (ISkill skill : skills) {
            skill.castSkill();  // 每個技能自己決定是否真正施放
        }
    }
}

4. 測試


public class ChainOfResponsibilityDemo {
    public static void main(String[] args) {
        Macro macro = new Macro();
        
        // 綁定順序:先冰血冷脈,後寒冰箭
        macro.bindSkill(new IceBloodFast());
        macro.bindSkill(new IceArrow());
        
        System.out.println("=== 第一次按鍵 ===");
        macro.castSkill();
        
        System.out.println("\n=== 立即再次按鍵(冰血冷脈應冷卻中) ===");
        macro.castSkill();
        
        System.out.println("\n=== 2分鐘後再次按鍵 ===");
        // 模擬時間跳躍(實際測試需等待或修改系統時間)
        // 這裡直接建立新實例演示
        Macro macro2 = new Macro();
        macro2.bindSkill(new IceBloodFast());
        macro2.bindSkill(new IceArrow());
        macro2.castSkill();
    }
}

執行結果


=== 第一次按鍵 ===
施放──》冰血冷脈
施放──》寒冰箭

=== 立即再次按鍵(冰血冷脈應冷卻中) ===
冰血冷脈:冷卻中... (119秒)
施放──》寒冰箭

=== 2分鐘後再次按鍵 ===
施放──》冰血冷脈
施放──》寒冰箭

標準責任鏈實作(含傳遞控制)

上述範例中每個技能必須執行。標準模式允許處理者決定是否傳遞給下一個:

抽象處理者


public abstract class AbstractHandler {
    protected AbstractHandler nextHandler;
    
    public void setNextHandler(AbstractHandler nextHandler) {
        this.nextHandler = nextHandler;
    }
    
    /** 模板方法:處理請求 */
    public final void handleRequest(Request request) {
        if (canHandle(request)) {
            doHandle(request);
        } else if (nextHandler != null) {
            nextHandler.handleRequest(request);  // 傳遞給下一個
        } else {
            // 鏈末端無人處理
            System.out.println("無處理者能處理請求:" + request);
        }
    }
    
    /** 子類實作:是否能處理此請求 */
    protected abstract boolean canHandle(Request request);
    
    /** 子類實作:具體處理邏輯 */
    protected abstract void doHandle(Request request);
}

具體處理者範例:請求審批流程


// 請求物件
public class Request {
    public enum Type { LEAVE, EXPENSE, PURCHASE }
    public Type type;
    public double amount;
    public String applicant;
    // getter/setter 省略
}

// 組長:處理請假、小額報銷
public class TeamLeaderHandler extends AbstractHandler {
    @Override
    protected boolean canHandle(Request request) {
        return request.type == Request.Type.LEAVE || 
               (request.type == Request.Type.EXPENSE && request.amount <= 1000);
    }
    
    @Override
    protected void doHandle(Request request) {
        System.out.println("組長審批通過:" + request.applicant + " 的 " + request.type);
    }
}

// 經理:處理大額報銷、採購
public class ManagerHandler extends AbstractHandler {
    @Override
    protected boolean canHandle(Request request) {
        return (request.type == Request.Type.EXPENSE && request.amount <= 10000) ||
               (request.type == Request.Type.PURCHASE && request.amount <= 50000);
    }
    
    @Override
    protected void doHandle(Request request) {
        System.out.println("經理審批通過:" + request.applicant + " 的 " + request.type + " 金額:" + request.amount);
    }
}

// 總監:處理超大額
public class DirectorHandler extends AbstractHandler {
    @Override
    protected boolean canHandle(Request request) {
        return request.type == Request.Type.EXPENSE || request.type == Request.Type.PURCHASE;
    }
    
    @Override
    protected void doHandle(Request request) {
        System.out.println("總監審批通過:" + request.applicant + " 的 " + request.type + " 金額:" + request.amount);
    }
}

建立鏈與測試


public class ApprovalChainDemo {
    public static void main(String[] args) {
        // 建立處理者
        AbstractHandler teamLeader = new TeamLeaderHandler();
        AbstractHandler manager = new ManagerHandler();
        AbstractHandler director = new DirectorHandler();
        
        // 串聯鏈:組長 → 經理 → 總監
        teamLeader.setNextHandler(manager);
        manager.setNextHandler(director);
        
        // 測試請求
        Request req1 = new Request();
        req1.type = Request.Type.LEAVE;
        req1.applicant = "小王";
        teamLeader.handleRequest(req1);
        
        Request req2 = new Request();
        req2.type = Request.Type.EXPENSE;
        req2.amount = 5000;
        req2.applicant = "小李";
        teamLeader.handleRequest(req2);
        
        Request req3 = new Request();
        req3.type = Request.Type.PURCHASE;
        req3.amount = 80000;
        req3.applicant = "小張";
        teamLeader.handleRequest(req3);
    }
}

進階變體

1. 純責任鏈 vs 不純責任鏈

  • 純: 每個請求只能被一個處理者處理,處理後不再傳遞
  • 不純: 處理後可選擇繼續傳遞(如 Servlet Filter、Log4j Appender)

2. 動態鏈構建


public class DynamicChainBuilder {
    public static AbstractHandler buildChain(List> handlerClasses) {
        AbstractHandler head = null, prev = null;
        for (Class clazz : handlerClasses) {
            try {
                AbstractHandler handler = clazz.getDeclaredConstructor().newInstance();
                if (head == null) head = handler;
                if (prev != null) prev.setNextHandler(handler);
                prev = handler;
            } catch (Exception e) { throw new RuntimeException(e); }
        }
        return head;
    }
}

3. 異步責任鏈


public interface AsyncHandler {
    CompletableFuture handleAsync(Request request);
}

// 使用 CompletableFuture 串聯
public class AsyncChainDemo {
    public static void main(String[] args) {
        AsyncHandler h1 = req -> CompletableFuture.runAsync(() -> { /* 處理 */ });
        AsyncHandler h2 = req -> CompletableFuture.runAsync(() -> { /* 處理 */ });
        
        // 串聯
        CompletableFuture chain = h1.handleAsync(req)
            .thenCompose(v -> h2.handleAsync(req));
    }
}

實務應用場景

場景處理者鏈說明
Servlet FilterEncodingFilter → AuthFilter → LogFilter → BusinessFilter請求預處理、認證、日誌、業務邏輯
Spring InterceptorpreHandle → Controller → postHandle → afterCompletion控制器前後攔截
Log4j/Logback AppenderConsoleAppender → FileAppender → DBAppender → EmailAppender日誌多重輸出
異常處理鏈ValidationExceptionHandler → BusinessExceptionHandler → GlobalExceptionHandler分層異常處理
支付風控BlacklistCheck → AmountCheck → VelocityCheck → ManualReview逐級攔截風險交易
GUI 事件傳遞Component → Container → Window → AppContextAWT/Swing 事件冒泡

最佳實踐建議

  1. 明確職責邊界: 每個處理者單一職責,避免邏輯重疊
  2. 避免循環引用: 建立鏈時檢查是否形成環
  3. 提供預設處理者: 鏈尾端兜底,防止請求丟失
  4. 考慮效能: 長鏈可能影響延遲,關鍵路徑優化
  5. 執行期可配置: 透過配置檔或註解動態組裝鏈,而非硬編碼
  6. 區分同步/異步: I/O 密集型處理者採用異步模式

結語

責任鏈模式透過將處理者串聯成鏈,實現請求發送者與接收者的解耦。無論是遊戲技能巨集、審批流程、Servlet Filter 還是日誌系統,核心思想都是:給多個物件處理請求的機會,避免發送者與接收者的強耦合。掌握純/不純鏈、動態構建、異步處理等變體,能靈活應對複雜業務場景。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved