文章目錄
- 簡介
- 問題
- 解決
- 代碼
- 核心改進點:
- 總結
簡介
責任鏈是一種行為設計模式,允許你把請求沿著處理者鏈進行發送。收到請求后,每個處理者均可對請求進行處理,或將其傳遞給鏈上的下個處理者。
問題
假如你正在開發一個訂單系統。 你希望限制系統訪問,只允許認證用戶創建訂單。 而管理員擁有所有訂單的完全訪問權限。
按照一般的開發思路,你會依次執行這些檢查。 只要接收到包含用戶憑據的請求,系統就可以嘗試認證。 如果認證失敗, 那就沒有必要再進行后續檢查了。如下圖。
接著,關于認證檢查的需求越來越多。比如為了不讓原始數據直接傳遞到訂單系統,需要在認證之后清理請求中的數據;比如為了對暴力密碼破解或者爬蟲請求進行限流,又需要在認證之后增加過濾來自同一 IP 地址的重復請求邏輯;比如為了提高系統響應速度,降低系統負載,又需要在請求發送給系統之前檢查有沒有緩存結果,如果沒有才會把請求發送給系統。如下圖,這部分邏輯就會越來越多,越來越混亂。
而且,修改某個檢查步驟可能也會影響其他的檢查步驟。 尤其是當你希望復用這些檢查步驟來保護其他系統時,你只能復制部分代碼, 因為這些系統只需要部分檢查步驟。
解決
與許多其他行為設計模式一樣, 責任鏈會把 特定行為轉換成 叫做處理者的獨立對象。 在上面示例里, 每個檢查步驟都可以被抽取成僅有單個方法的類, 并執行檢查操作。 請求和對應數據會被作為參數傳遞給這個方法。
這個模式建議你把這些處理者連成一條鏈。 鏈上的每個處理者都有一個成員變量來保存對于下一個處理者的引用。 除了處理請求外, 處理者還負責沿著鏈傳遞請求。 請求會在鏈上移動, 直到所有處理者都有機會對他進行處理。
更重要的是: 處理者可以決定不再沿著鏈傳遞請求, 這就可以高效地取消所有后面的處理步驟。
在我們的訂單系統示例中, 處理者會在進行請求處理工作后決定要不要繼續沿著鏈傳遞請求。 如果請求中包含正確的數據, 所有處理者都會執行自己的主要行為, 無論這個行為是身份驗證還是數據緩存。
不過還有一種稍微不同的方式 (也是更經典一種), 那就是處理者接收到請求后自己決定是否能夠對其進行處理。 如果自己能夠處理, 處理者就不再繼續傳遞請求。 因此在這種情況下, 每個請求要么最多有一個處理者進行處理, 要么沒有任何處理者進行處理。 在處理圖形用戶界面元素棧中的事件時, 這種方式非常常見。另外,其實很多設計模式都是從很早用Java寫客戶端界面引出的解決方案。
代碼
// Handler接口定義處理契約
public interface Handler {void handle(Request request) throws AuthException;
}// BaseHandler實現鏈式傳遞邏輯
public abstract class BaseHandler implements Handler {private Handler next;public BaseHandler setNext(Handler next) {this.next = next;return this;}protected void passToNext(Request request) throws AuthException {if (next != null) next.handle(request); // 核心鏈式調用邏輯}
}// 具體處理者1:用戶認證
class UserAuthHandler extends BaseHandler {@Overridepublic void handle(Request request) throws AuthException {if (!validateUser(request.getUserId())) {throw new AuthException("用戶未登錄");}passToNext(request); // 驗證成功移交后續處理}
}// 具體處理者2:權限校驗
class PermissionHandler extends BaseHandler {@Override public void handle(Request request) throws AuthException {if (!checkAdminPermission(request.getUserId())) {throw new PermissionException("權限不足");}passToNext(request);}
}// Client動態組合處理鏈
public class OrderService {private Handler chain;public OrderService() {this.chain = new UserAuthHandler().setNext(new PermissionHandler()); // 靈活配置處理順序}public void createOrder(Request request) {chain.handle(request); // 統一入口觸發處理鏈// 執行業務邏輯...}
}
核心改進點:
- 解耦檢查邏輯:每個安全檢查獨立成類,通過setNext組合鏈式結構
- 動態擴展性:新增日志檢查僅需創建LogHandler并插入鏈中任意位置
- 復用性增強:在PaymentService中可重用UserAuthHandler而不需要重復驗證代碼
總結
- (Handler)聲明了所有具體處理者的通用接口。這個接口通常只包含單個方法,用于請求處理,但有時它還會包含一個設置鏈上 下一個處理者 的方法。
- (Base Handler)是一個可選的類,你可以把所有處理者共用的樣本代碼放在里面。 通常情況下,這個類里定義了一個保存下個處理者引用的成員變量。客戶端可以把下個處理者傳遞給上個處理者的構造函數或用setter方法 來創建鏈。這個類還可以實現默認的處理行為: 比如確定下個處理者存在后再把請求傳遞給它。
- (Concrete Handlers)包含處理請求的實際代碼。每 個處理者接收到請求后,都必須決定要不要處理,以及要不要沿 著鏈傳遞請求。 處理者通常是獨立并且不可變的,需要通過構造函數一次性地獲得 所有必要的數據。
- (Client) 可根據程序邏輯一次性或者動態地生成鏈。 需要注意一下,請求可以發送給鏈上的任意一個處理者,不一定是第一個處理者。