设计模式第七章(责任链模式) - 实践

设计模式第七章(责任链模式)

前言

责任链模式(Chain of Responsibility Pattern)是一种行为型设计模式,其核心思想是:将多个请求处理者(对象)连接成一条 “链”,当一个请求产生时,会沿着这条链依次传递,直到链中的某个处理者能够处理该请求为止

核心目的

避免请求的发送者与接收者之间形成直接耦合 —— 发送者不需要知道具体哪个对象会处理请求,只需将请求传递给链的起始端,由链自行决定谁来处理。

关键角色

  1. 抽象处理者(Handler)

    定义一个处理请求的接口(通常包含一个处理方法),并持有一个 “下一个处理者” 的引用(即链中的下一个节点)。

  2. 具体处理者(ConcreteHandler)

    实现抽象处理者的接口,负责判断自己是否能处理当前请求:

    • 如果能处理,则直接处理;
    • 如果不能处理,则将请求传递给 “下一个处理者”。
  3. 客户端(Client)

    创建具体处理者对象,按顺序组装成责任链,并发起请求(将请求传递给链的第一个处理者)。

适用场景

  • 多个对象都可能处理同一请求,但具体由谁处理需要动态决定(如权限校验、日志记录、异常处理等);
  • 希望避免请求发送者与处理者的直接关联(解耦);
  • 需要动态调整处理者的顺序或增减处理者(如扩展功能时)。

优点

  1. 解耦:请求发送者无需知道谁会处理请求,处理者也无需知道请求的全貌;
  2. 灵活:可动态调整链中处理者的顺序或增减处理者(符合 “开闭原则”);
  3. 简化对象:每个处理者只需关注自己能处理的请求,职责单一。

缺点

  1. 效率风险:如果链过长,请求可能需要经过多个处理者才能被处理,影响性能;
  2. 无人处理:如果链中所有处理者都无法处理请求,可能导致请求 “丢失”(需提前设计兜底逻辑)。

Java 中的常见应用

  • Servlet 中的 Filter 链:多个 Filter 按顺序处理请求,每个 Filter 可选择处理或传递给下一个;
  • Spring 中的拦截器(Interceptor):请求经过多个拦截器组成的链,依次执行预处理、后处理等操作。

通过责任链模式,代码会更具灵活性和可扩展性,尤其适合处理流程化、多步骤的业务场景。

责任链基本使用

​ 我们现在需要接受前端传过来的一个user对象,里面有name 和 age 属性,我们需要校验这两个属性的合法性,传统的写法就是那个用户对象,一个一个的去遍历进行校验,那么我们可以写一个注解,放到属性上面,然后调用校验方法

注解类

  • length

    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Length {
    int value();
    }
  • Max

    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Max {
    int value();
    }
  • Min

    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Min {
    int value();
    }

实体类 user

public class User {
@Length(4)
private String name;
@Max(17)
@Min(17)
private Integer age;
public User(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}

异常类

public class ValidatorException extends RuntimeException{
public ValidatorException(String message) {
super(message);
}
}

校验器类

public class Validator {
public void validate(Object bean) throws Exception{
Class<?> aClass = bean.getClass();
  for (Field field : aClass.getDeclaredFields()) {
  field.setAccessible(true);
  ValidateChain chain = buildValidateChain(field);
  chain.validate(field.get(bean));
  }
  }
  /**
  *  组链的过程
  * @param field
  * @return
  */
  private ValidateChain buildValidateChain(Field field) {
  ValidateChain chain = new ValidateChain();
  Max max = field.getAnnotation(Max.class);
  if (max != null) {
  chain.addLastValidaHandler(new MaxValidateHandler(max.value()));
  }
  Min min = field.getAnnotation(Min.class);
  if (min != null) {
  chain.addLastValidaHandler(new MinValidateHandler(min.value()));
  }
  Length length = field.getAnnotation(Length.class);
  if (length != null) {
  chain.addLastValidaHandler(new LengthValidateHandler(length.value()));
  }
  return chain;
  }
  }

ValidateChain 类

public class ValidateChain {
private final List<ValidateHandler> validateHandlers = new ArrayList<>();
  public void validate(Object object) {
  for (ValidateHandler handler : validateHandlers) {
  handler.validate(object);
  }
  }
  public void addLastValidaHandler(ValidateHandler handler) {
  validateHandlers.add(handler);
  }
  }

校验类

public interface ValidateHandler {
void validate(Object object)throws ValidatorException;
}
public class LengthValidateHandler implements ValidateHandler{
private final Integer length;
public LengthValidateHandler(Integer length) {
this.length = length;
}
@Override
public void validate(Object object) throws ValidatorException {
if (object instanceof String strValue) {
if (strValue.length() != length) {
throw new ValidatorException("当前值长度:"+(strValue.length()) + ",期望的值:"+ length);
}
}
}
}
public class MaxValidateHandler implements ValidateHandler{
private final Integer max;
public MaxValidateHandler(Integer max) {
this.max = max;
}
@Override
public void validate(Object object) throws ValidatorException {
if (object instanceof Integer intValue) {
if (intValue > max) {
throw new ValidatorException("当前值过大:"+intValue + ",期望的值:"+max);
}
}
public class MinValidateHandler implements ValidateHandler{
private final Integer min;
public MinValidateHandler(Integer min) {
this.min = min;
}
@Override
public void validate(Object object) throws ValidatorException {
if (object instanceof Integer intValue) {
if (intValue < min) {
throw new ValidatorException("当前值过小:"+intValue + ",期望的值:"+min);
}
}
}
}

测试类

public class Main {
public static void main(String[] args) throws Exception {
User user = new User("zhangsan", 18);
Validator validator = new Validator();
validator.validate(user);
//当前需求是,我需要把错误的信息都返回,而不是直接中断了
}
}

在这里插入图片描述

遗留问题点

  • 有的属性上面是有多个校验的,但是只能显示一个错误信息,我需要把所有的错误都显示出来,传统解决方式,在验证里面加上 try cath 。

    public void validate(Object object) {
    List<String> errList = new ArrayList<>();
      for (ValidateHandler handler : validateHandlers) {
      try {
      handler.validate(object);
      } catch (ValidatorException e) {
      errList.add(e.getMessage());
      }
      }
      if (!errList.isEmpty()) {
      throw new ValidatorException(errList.toString());
      }
      }

责任链升级

我们刚刚看到,校验我们采用的是抛异常的方式,那么我们就没有办法将一个属性的错误全部暴露出来,这个时候,我们需要有一个上下文的支持,用来存储上下文异常信息;

上下文类

public class ValidatorContext {
private final List<String> errMsgList = new ArrayList<>();
  private boolean stop = false;
  private int index = 0;
  private Object value;
  public ValidatorContext(Object value) {
  this.value = value;
  }
  public Object getValue() {
  return value;
  }
  public void appendError(String error) {
  errMsgList.add(error);
  }
  public void stopChain() {
  this.stop = true;
  }
  public boolean shouldStop() {
  return this.stop;
  }
  // 责任了继续往下走
  public void doNext(Object value) {
  index++;
  this.value = value;
  }
  public int currentIndex() {
  return index;
  }
  // 抛出错误异常
  public void throwExceptionIfNotSuccess() {
  if (!errMsgList.isEmpty()) {
  throw new ValidatorException(errMsgList.toString());
  }
  }
  }

在这里插入图片描述

支持next才调用下一个校验

该方式类似于 servelt 中的 dofilter,每次我们使用完了后,需要继续调用一下才进行下一步,否则就不进行校验;

public class ValidateChain {
private final List<ValidateHandler> validateHandlers = new ArrayList<>();
  // 校验上下文
  public void validate(Object object) {
  ValidatorContext context = new ValidatorContext(object);
  while (true) {
  int index = context.currentIndex();
  if (index == validateHandlers.size()) {
  break;
  }
  ValidateHandler handler = validateHandlers.get(index);
  handler.validate(context.getValue(),context);
  if (index == context.currentIndex()) {
  // 说明没有调用 doNext函数
  break;
  }
  }
  //        for (ValidateHandler handler : validateHandlers) {
  //            handler.validate(object,context);
  //            if (context.shouldStop()) {
  //                break;
  //            }
  //        }
  context.throwExceptionIfNotSuccess();
  }
  public void addLastValidaHandler(ValidateHandler handler) {
  validateHandlers.add(handler);
  }
  }
测试

我们修改MaxValidateHandler,然后在校验里面关闭 doNext值

@Override
public void validate(Object object, ValidatorContext context) throws ValidatorException {
    if (object instanceof Integer intValue) {
        if (intValue > max) {
            context.appendError("当前值过大:"+intValue + ",期望的值:"+max);
        }
    }
    //context.doNext(object);
}

在这里插入图片描述

支持修改值的上下文

我们在任意步骤都可以对值进来修改,从而达到我们的目的;

public void validate(Object object) {
   ValidatorContext context = new ValidatorContext(object);
    while (true) {
        int index = context.currentIndex();
        if (index == validateHandlers.size()) {
            break;
        }
        ValidateHandler handler = validateHandlers.get(index);
        handler.validate(context.getValue(),context);
        if (index == context.currentIndex()) {
            // 说明没有调用 doNext函数
            break;
        }
    }
测试
@Override
public void validate(Object object, ValidatorContext context) throws ValidatorException {
if (object instanceof Integer intValue) {
if (intValue > max) {
context.appendError("当前值过大:"+intValue + ",期望的值:"+max);
}
}
context.doNext(20);
}

在这里插入图片描述

拓展

其实我们可以在上下文里面,加入一些其他的标识,用一个map存储起来,当传入到任意的过滤器中的时候,我们都可以进行获取,从而做一些额外的操作

servlet 中的 Filter 应用

public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain)
throws IOException, ServletException;

在这里插入图片描述

实际使用

我们可以在filte中增加日志的追溯id

public class HttpFilter implements Filter {
private Logger logger = LoggerFactory.getLogger(HttpFilter.class);
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse httpServletResponse = (HttpServletResponse) res;
//跨域的header设置
httpServletResponse.setHeader("Access-control-Allow-Origin", request.getHeader("Origin"));
httpServletResponse.setHeader("Access-Control-Allow-Methods", request.getMethod());
httpServletResponse.setHeader("Access-Control-Allow-Credentials", "true");
httpServletResponse.setHeader("Access-Control-Allow-Headers", request.getHeader("Access-Control-Request-Headers"));
try{
if (StaticSourceFilter.isStaticSource(request.getRequestURI())) {
chain.doFilter(req, res);
return;
}
ServletRequest requestWrapper = null;
if (req instanceof HttpServletRequest) {
//requestWrapper = new RequestWrapperFilter((HttpServletRequest) req);
}
String traceId = LogUtil.getTraceId();
ipLog(request, traceId);
MDC.put("uuid", traceId);
if (requestWrapper == null) {
chain.doFilter(req, res);
} else {
chain.doFilter(requestWrapper, res);
}
}finally {
MDC.clear();
}
}

总结

我们看到最终我们需要调用 chain.doFilter(requestWrapper, res); 方法进行下一步的传递操作

posted @ 2025-11-12 04:00  ycfenxi  阅读(4)  评论(0)    收藏  举报