掌握設計模式--責任鏈模式
責任鏈模式(Chain of Responsibility)
責任鏈模式(Chain of Responsibility)是一種行為型設計模式,旨在通過將請求沿著一系列處理者傳遞,形成一條處理鏈,直到請求被處理鏈上特定的結點處理為止。它允許多個對象有機會處理請求,而不需要明確指定哪個對象將處理該請求。每個處理者包含對下一個處理者的引用,如果當前處理者無法處理請求,它會將請求傳遞給下一個處理者。這樣可以將請求的處理職責鏈式地分配給多個處理者,而不需要將它們緊密耦合。

組成部分
- 抽象處理者(Handler):定義一個處理請求的接口,并且持有一個指向下一個處理者的引用。如果當前處理者無法處理請求,就將其傳遞給下一個處理者。
- 具體處理者(ConcreteHandler):實現抽象處理者的接口,處理請求。如果無法處理,則傳遞給下一個處理者。
- 客戶端(Client):發起請求,并將請求發送到責任鏈的起始點。
案例實現
假設我們需要處理不同等級的日志信息,并根據不同的日志等級,將日志信息寫入不同的日志文件。日志等級包括info、debug、error和warning 日志級別。使用者只需指定日志級別,即可在責任鏈對象中自動處理對應的日志信息。
注:為了代碼的實現簡單,這里不編寫具體的寫入文件IO流操作,只是控制臺輸出。
設計思路
handleLog方法:每個LogHandler只關注自己的日志級別,并在處理完成后調用nextHandler的handleLog方法;- 責任鏈的鏈式處理:
LoggerChain類負責維護日志處理器的順序,并且通過一個nextHandler參數將責任傳遞給下一個處理器,實現責任的傳遞; - 靈活擴展:不需要每個處理器顯式管理
nextHandler,在理想情況下只需要維護日志枚舉類即可。
案例類圖

1. 定義LogHandler接口
在接口中,handleLog 方法接收一個 nextHandler 參數,決定是否將日志傳遞給下一個處理器。
public interface LogHandler {
void handleLog(LoggerEnum logLevel, String message, LogHandler nextHandler);
}
2. 具體日志處理器實現
每個日志處理器只關心自己負責的日志等級,如果當前處理器能處理,則輸出日志,并將控制權交給下一個處理器。
public class InfoLogHandler implements LogHandler {
@Override
public void handleLog(LoggerEnum logLevel, String message, LogHandler nextHandler) {
if (logLevel.shouldLog(LoggerEnum.INFO)) {
System.out.println(this.getClass().getSimpleName() + ">> INFO: " + message);
}
// 如果存在下一個處理器
if (nextHandler != null) {
nextHandler.handleLog(logLevel, message, nextHandler);
}
}
}
class DebugLogHandler implements LogHandler {
@Override
public void handleLog(LoggerEnum logLevel, String message, LogHandler nextHandler) {
if (logLevel.shouldLog(LoggerEnum.DEBUG)) {
System.out.println(this.getClass().getSimpleName() + ">> DEBUG: " + message);
}
// 如果存在下一個處理器
if (nextHandler != null) {
nextHandler.handleLog(logLevel, message, nextHandler);
}
}
}
class ErrorLogHandler implements LogHandler {
@Override
public void handleLog(LoggerEnum logLevel, String message, LogHandler nextHandler) {
if (logLevel.shouldLog(LoggerEnum.ERROR)) {
System.out.println(this.getClass().getSimpleName() + ">> ERROR: " + message);
}
// 如果存在下一個處理器
if (nextHandler != null) {
nextHandler.handleLog(logLevel, message, nextHandler);
}
}
}
class WarningLogHandler implements LogHandler {
@Override
public void handleLog(LoggerEnum logLevel, String message, LogHandler nextHandler) {
if (logLevel.shouldLog(LoggerEnum.WARNING)) {
System.out.println(this.getClass().getSimpleName() + ">> WARNING: " + message);
}
// 如果存在下一個處理器
if (nextHandler != null) {
nextHandler.handleLog(logLevel, message, nextHandler);
}
}
}
3. 創建處理器鏈
public class LoggerChain implements LogHandler{
private int currentPosition = 0;
private List<LogHandler> handlers = new ArrayList<>();
// 初始化日志責任鏈時可以結合創建型設計模式來動態實現,符合開閉原則
public LoggerChain() {
// 自動創建并排序處理器,按優先級從低到高
handlers.add(new InfoLogHandler());
handlers.add(new DebugLogHandler());
handlers.add(new ErrorLogHandler());
handlers.add(new WarningLogHandler());
}
// 處理日志
public void log(LoggerEnum logLevel, String message) {
this.handleLog(logLevel,message,null);
}
@Override
public void handleLog(LoggerEnum logLevel, String message, LogHandler nextHandler) {
if (currentPosition == handlers.size()) {
// 退出責任鏈
currentPosition = 0;
}else{
LogHandler firstHandler = handlers.get(currentPosition++);
firstHandler.handleLog(logLevel, message, this);
}
}
}
4. 日志等級枚舉
LoggerEnum 枚舉定義了不同的日志等級
public enum LoggerEnum {
INFO(1), // 信息日志
DEBUG(2), // 調試日志
ERROR(3), // 錯誤日志
WARNING(4); // 警告日志
private final int priority;
LoggerEnum(int priority) {
this.priority = priority;
}
public int getPriority() {
return priority;
}
// 判斷當前等級是否符合輸出要求:比如Debug級別的日志可以輸出debug和info的日志
public boolean shouldLog(LoggerEnum currentLogLevel) {
return this.priority >= currentLogLevel.getPriority();
}
}
5. 測試類
public class LoggerTest {
public static void main(String[] args) {
LoggerChain loggerChain = new LoggerChain();
// 模擬不同日志級別的請求
System.out.println("日志級別: INFO");
loggerChain.log(LoggerEnum.INFO, "這是 info 信息.");
System.out.println("\n日志級別: DEBUG");
loggerChain.log(LoggerEnum.DEBUG, "這是 debug 信息.");
System.out.println("\n日志級別: ERROR");
loggerChain.log(LoggerEnum.ERROR, "這是 error 信息.");
System.out.println("\n日志級別: WARNING");
loggerChain.log(LoggerEnum.WARNING, "這是 warning 信息.");
}
}
執行結果

在這個案例中,初始化日志責任鏈時可以結合創建型設計模式來動態實現,才符合開閉原則,新增或刪除日志級別時只需要維護枚舉類即可。將控制臺輸出改為IO流寫入文件,即可實現不同日志級別的信息寫入到不同的日志文件。
優缺點和應用場景
優點
- 降低耦合度:客戶端不需要知道哪個具體的對象會處理請求,處理請求的對象可以動態變化;
- 擴展性強:新的處理器可以很容易地被添加到責任鏈中,且不需要修改現有的代碼;
- 職責分離:每個處理者只關注自己能處理的邏輯,職責清晰。
缺點
- 鏈過長時可能造成性能問題:請求可能在鏈中經過多個處理者,這可能導致性能上的損耗,尤其是責任鏈較長時;
- 調試復雜性:由于請求被多個處理者處理,調試時可能較難追蹤請求的流轉路徑;
- 請求可能永遠無法得到處理:如果責任鏈中的所有處理器都沒有處理該請求,則請求會被忽略或終止。這種情況可能會導致某些請求得不到預期的處理結果,需要在設計時注意鏈的完整性和錯誤處理機制。
應用場景
-
日志記錄:責任鏈模式可以用于日志記錄的處理。不同的日志級別(例如,INFO、DEBUG、ERROR)可以通過責任鏈模式傳遞,依次被不同的日志處理器(如控制臺日志、文件日志、網絡日志等)處理。
-
權限校驗:在復雜的權限校驗中,不同的權限校驗可以作為責任鏈的一部分,依次處理。每個處理器檢查不同的權限要求,直到滿足條件或結束。
例子:在SpringSecurity中,訪問控制或權限校驗可以通過過濾鏈模式來實現。例如,檢查用戶是否擁有訪問某個頁面的權限,可以通過多個權限處理器(如角色權限、用戶權限、IP 白名單等)進行逐層處理。
-
請求過濾:Servlet 的過濾器鏈(Filter Chain),每個過濾器負責請求的某個方面(例如,身份驗證、權限檢查、日志記錄等)。請求被傳遞到鏈中的下一個過濾器,直到最終響應。
-
表單驗證:表單驗證可以通過責任鏈模式進行處理,每個驗證器可以處理不同的驗證規則,直到表單滿足所有驗證條件。
例子:在表單提交時,可以有多個驗證器(如空值檢查、格式驗證、長度驗證、范圍驗證等),每個驗證器都負責處理不同的驗證邏輯。
- 數據處理管道:在數據處理流程中,責任鏈模式適合于處理多個步驟的數據流。每個步驟可以視為一個處理器,負責對數據進行某種操作,數據會被傳遞到下一個步驟。
例子:數據清洗和轉換流程中,每個數據清洗步驟(如去除空值、格式化、轉換編碼等)可以作為責任鏈的一部分,按順序處理數據。
責任鏈模式的應用
Spring Security的過濾鏈(Filter Chain)是責任鏈模式的一種典型實現。它是一個按順序執行的過濾器集合,負責攔截和處理HTTP請求,以實現認證、授權、安全控制等功能,并且支持自定義過濾器插入到Spring Security的過濾鏈中,從而實現自定義的安全處理邏輯,使得Spring Security變得更加靈活。
總結
責任鏈設計模式是一種行為設計模式,其核心在于將多個處理對象連接成一條鏈,允許請求沿鏈傳遞,直到有一個處理者能夠處理該請求,從而實現請求的解耦和動態的處理順序管理,并且處理者易于擴展,使得程序更加靈活。

需要查看往期設計模式文章的,可以在個人主頁中或者文章開頭的集合中查看,可關注我,持續更新中。。。

浙公網安備 33010602011771號