簡單地介紹:
責任鏈(Chain Of Responsibility)使多個對象都有機會處理請求,從而避免請求的發送者和接收者之間的耦合關係。將這些對象連成一條鏈,並沿着這
條鏈發送該請求,直到有一個對象處理它爲止。
沒有使用時的問題:
在介紹這個設計模式之前,先看看不使用這個模式,我們的代碼會是什麼樣子的.
public static void main(String[] args) { LoginRequest request = new LoginRequest("age","年齡校驗"); } public void checkLoginParam(LoginRequest request){ if ("name".equals(request.getRequestType())){ // 姓名的檢驗邏輯 }else if ("age".equals(request.getRequestType())){ // 年齡的校驗邏輯 } //其他的校驗邏輯............... }
很容易發現 代碼會隨着後期校驗邏輯的增加 越來越長,越複雜。耦合性很高,而且新增校驗邏輯需要修改以前的代碼判斷分支,也違背了單一職責原則和開閉原則。
使用後的效果:
如何解決這個複雜校驗邏輯,還能滿足設計原則呢?
推薦使用 責任鏈模式,當然,責任鏈模式的實現方法很多
javax.servlet.Filter#doFilter 使用 過濾器數組 移動數組索引實現 調用下一個過濾器 (核心類 ApplicationFilterChain)
我先採用最簡單的嵌套構造的方式來實現:
先看下類圖,有個總體的認知
-
核心 任務處理父類
-
LoginHandler :定義處理請求的接口,並且實現後繼鏈(handler)
public abstract class LoginHandler {
protected LoginHandler handler;
public LoginHandler(LoginHandler handler){
this.handler = handler;
}
protected abstract void handlerLoginRequest(LoginRequest request);
}
具體的處理器類:
1. 年齡校驗邏輯
public class LoginAgeHandler extends LoginHandler {
public LoginAgeHandler(LoginHandler handler){
super(handler);
}
@Override
protected void handlerLoginRequest(LoginRequest request) {
if ("age".equals(request.getRequestType())){
System.out.println("LoginAgeHandler----》 " + request.getTaskName());
return;
}
if (null != handler){
handler.handlerLoginRequest(request);
}
}
}
2. 姓名校驗邏輯:
public class LoginNameHandler extends LoginHandler {
public LoginNameHandler(LoginHandler handler){
super(handler);
}
/**
* 處理完成自己的邏輯後 判斷是否需要交給下一個處理器來處理
* @param request
*/
@Override
protected void handlerLoginRequest(LoginRequest request) {
if ("name".equals(request.getRequestType())){
// query user from db, if exist execute next stop
System.out.println("LoginNameHandler--》" + request.getTaskName());
}
// 交給下一個處理器處理 請求任務
if (null != handler){
handler.handlerLoginRequest(request);
}
}
}
待處理的任務類:
public class LoginRequest {
private String requestType;
private String taskName;
public LoginRequest(String requestType, String taskName) {
this.requestType = requestType;
this.taskName = taskName;
}
public String getRequestType() {
return requestType;
}
public String getTaskName() {
return taskName;
}
}
最後,我們做一下調用測試:
package com.cheri.designpattern.cases.action;
/**
*
* 嵌套過濾器,內部依次判斷是否存在 執行過濾器
* @author Aaron Du
* @version V1.0
* @date 2020/5/1 15:08
**/
public class Client {
public static void main(String[] args) {
LoginRequest request1 = new LoginRequest("name","用戶名檢查");
LoginRequest request2 = new LoginRequest("age","年齡檢查");
LoginHandler ageHandler = new LoginAgeHandler(null);
LoginHandler nameHandler = new LoginNameHandler(ageHandler);
nameHandler.handlerLoginRequest(request1);
nameHandler.handlerLoginRequest(request2);
}
}
可以清楚的看到,對應處理器只處理自己負責的處理邏輯。
我們看JDK如何使用責任鏈模式的:
一般我們使用一個過濾器時都是如下處理:實現Filter接口重寫doFilter方法
public class MyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// 自己過濾器處理邏輯
// 調用下一個過濾器
chain.doFilter(request,response);
}
@Override
public void destroy() {
}
}
在Tomcat容器中,會將所有的過濾器加載到 ApplicationFilterChain中的
private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0]數組中
下面是添加過濾器到數組的方法:
當我們調用
chain.doFilter(request,response);來調用下一個過濾器時,內部會調用
ApplicationFilterChain的doFilter方法
我們會看到,內部會通過數組索引的遞增來逐個調用下一個過濾器,實現過濾器逐個調用的效果。這個是過濾器Filter對
責任鏈模式的實現方法。
總結:1.要有待處理的任務對象。
2.要有統一的處理器基類 且 基類中要維護任務和一個抽象的處理方法,不同的子類都實現不同的處理邏輯。
3. 執行鏈的維護可以使用數據也可以採用構造嵌套的方式