責任鏈模式將請求的發送者與接收者解耦,讓多個物件都有機會處理請求,將這些物件串成一條鏈,沿著鏈傳遞請求,直到有物件處理為止。
場景:一鍵巨集綁定多個技能,按順序嘗試施放,技能冷卻中則跳過嘗試下一個。
public interface ISkill {
void castSkill();
}
// 無冷卻技能
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("施放──》冰血冷脈");
}
}
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(); // 每個技能自己決定是否真正施放
}
}
}
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);
}
}
public class DynamicChainBuilder {
public static AbstractHandler buildChain(List> handlerClasses) {
AbstractHandler head = null, prev = null;
for (Class extends AbstractHandler> 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;
}
}
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 Filter | EncodingFilter → AuthFilter → LogFilter → BusinessFilter | 請求預處理、認證、日誌、業務邏輯 |
| Spring Interceptor | preHandle → Controller → postHandle → afterCompletion | 控制器前後攔截 |
| Log4j/Logback Appender | ConsoleAppender → FileAppender → DBAppender → EmailAppender | 日誌多重輸出 |
| 異常處理鏈 | ValidationExceptionHandler → BusinessExceptionHandler → GlobalExceptionHandler | 分層異常處理 |
| 支付風控 | BlacklistCheck → AmountCheck → VelocityCheck → ManualReview | 逐級攔截風險交易 |
| GUI 事件傳遞 | Component → Container → Window → AppContext | AWT/Swing 事件冒泡 |
責任鏈模式透過將處理者串聯成鏈,實現請求發送者與接收者的解耦。無論是遊戲技能巨集、審批流程、Servlet Filter 還是日誌系統,核心思想都是:給多個物件處理請求的機會,避免發送者與接收者的強耦合。掌握純/不純鏈、動態構建、異步處理等變體,能靈活應對複雜業務場景。