简单地介绍:
责任链( 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. 执行链的维护可以使用数据也可以采用构造嵌套的方式