Skip to content

责任链模式

基础概念

什么是责任链模式?

答案: 责任链模式(Chain of Responsibility Pattern)为请求创建一个接收者对象的链,每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把请求传给下一个接收者。

使用场景

  • 多个对象可以处理同一请求
  • 不明确指定接收者
  • 动态指定处理者集合

优点

  • 降低耦合度
  • 增强灵活性
  • 简化对象

缺点

  • 请求可能不被处理
  • 性能影响
  • 调试困难

实现方式

基本实现?

答案:

java
// 抽象处理者
public abstract class Handler {
    protected Handler nextHandler;

    public void setNextHandler(Handler nextHandler) {
        this.nextHandler = nextHandler;
    }

    public abstract void handleRequest(int level);
}

// 具体处理者A
public class ConcreteHandlerA extends Handler {
    @Override
    public void handleRequest(int level) {
        if (level <= 1) {
            System.out.println("处理者A处理请求");
        } else if (nextHandler != null) {
            nextHandler.handleRequest(level);
        }
    }
}

// 具体处理者B
public class ConcreteHandlerB extends Handler {
    @Override
    public void handleRequest(int level) {
        if (level <= 2) {
            System.out.println("处理者B处理请求");
        } else if (nextHandler != null) {
            nextHandler.handleRequest(level);
        }
    }
}

// 具体处理者C
public class ConcreteHandlerC extends Handler {
    @Override
    public void handleRequest(int level) {
        if (level <= 3) {
            System.out.println("处理者C处理请求");
        } else {
            System.out.println("无法处理");
        }
    }
}

// 使用
Handler handlerA = new ConcreteHandlerA();
Handler handlerB = new ConcreteHandlerB();
Handler handlerC = new ConcreteHandlerC();

handlerA.setNextHandler(handlerB);
handlerB.setNextHandler(handlerC);

handlerA.handleRequest(1);  // A处理
handlerA.handleRequest(2);  // B处理
handlerA.handleRequest(3);  // C处理

实际应用

请假审批

答案:

java
// 请假请求
public class LeaveRequest {
    private String name;
    private int days;

    public LeaveRequest(String name, int days) {
        this.name = name;
        this.days = days;
    }

    public String getName() {
        return name;
    }

    public int getDays() {
        return days;
    }
}

// 抽象审批者
public abstract class Approver {
    protected Approver nextApprover;

    public void setNextApprover(Approver nextApprover) {
        this.nextApprover = nextApprover;
    }

    public abstract void approve(LeaveRequest request);
}

// 组长(1天以内)
public class TeamLeader extends Approver {
    @Override
    public void approve(LeaveRequest request) {
        if (request.getDays() <= 1) {
            System.out.println("组长批准" + request.getName() + "请假" + request.getDays() + "天");
        } else if (nextApprover != null) {
            nextApprover.approve(request);
        }
    }
}

// 经理(3天以内)
public class Manager extends Approver {
    @Override
    public void approve(LeaveRequest request) {
        if (request.getDays() <= 3) {
            System.out.println("经理批准" + request.getName() + "请假" + request.getDays() + "天");
        } else if (nextApprover != null) {
            nextApprover.approve(request);
        }
    }
}

// 总监(7天以内)
public class Director extends Approver {
    @Override
    public void approve(LeaveRequest request) {
        if (request.getDays() <= 7) {
            System.out.println("总监批准" + request.getName() + "请假" + request.getDays() + "天");
        } else {
            System.out.println("请假天数过多,拒绝");
        }
    }
}

// 使用
Approver teamLeader = new TeamLeader();
Approver manager = new Manager();
Approver director = new Director();

teamLeader.setNextApprover(manager);
manager.setNextApprover(director);

teamLeader.approve(new LeaveRequest("张三", 1));  // 组长批准
teamLeader.approve(new LeaveRequest("李四", 3));  // 经理批准
teamLeader.approve(new LeaveRequest("王五", 7));  // 总监批准

日志处理

答案:

java
// 日志级别
public enum LogLevel {
    DEBUG, INFO, WARN, ERROR
}

// 抽象日志处理器
public abstract class LogHandler {
    protected LogLevel level;
    protected LogHandler nextHandler;

    public LogHandler(LogLevel level) {
        this.level = level;
    }

    public void setNextHandler(LogHandler nextHandler) {
        this.nextHandler = nextHandler;
    }

    public void log(LogLevel level, String message) {
        if (this.level.ordinal() <= level.ordinal()) {
            write(message);
        }
        if (nextHandler != null) {
            nextHandler.log(level, message);
        }
    }

    protected abstract void write(String message);
}

// 控制台日志处理器
public class ConsoleLogHandler extends LogHandler {
    public ConsoleLogHandler(LogLevel level) {
        super(level);
    }

    @Override
    protected void write(String message) {
        System.out.println("Console: " + message);
    }
}

// 文件日志处理器
public class FileLogHandler extends LogHandler {
    public FileLogHandler(LogLevel level) {
        super(level);
    }

    @Override
    protected void write(String message) {
        System.out.println("File: " + message);
    }
}

// 邮件日志处理器
public class EmailLogHandler extends LogHandler {
    public EmailLogHandler(LogLevel level) {
        super(level);
    }

    @Override
    protected void write(String message) {
        System.out.println("Email: " + message);
    }
}

// 使用
LogHandler consoleHandler = new ConsoleLogHandler(LogLevel.DEBUG);
LogHandler fileHandler = new FileLogHandler(LogLevel.WARN);
LogHandler emailHandler = new EmailLogHandler(LogLevel.ERROR);

consoleHandler.setNextHandler(fileHandler);
fileHandler.setNextHandler(emailHandler);

consoleHandler.log(LogLevel.DEBUG, "调试信息");  // 只有Console输出
consoleHandler.log(LogLevel.WARN, "警告信息");   // Console和File输出
consoleHandler.log(LogLevel.ERROR, "错误信息");  // 全部输出

Servlet Filter

Filter责任链?

答案:

java
// Filter接口
public interface Filter {
    void doFilter(ServletRequest request, ServletResponse response, FilterChain chain);
}

// 编码过滤器
public class EncodingFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");
        chain.doFilter(request, response);  // 传递给下一个过滤器
    }
}

// 认证过滤器
public class AuthFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
        String token = request.getParameter("token");
        if (token != null) {
            chain.doFilter(request, response);
        } else {
            response.getWriter().write("未授权");
        }
    }
}

// 日志过滤器
public class LogFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
        System.out.println("请求开始");
        chain.doFilter(request, response);
        System.out.println("请求结束");
    }
}

Spring中的责任链

Spring Interceptor?

答案:

java
public interface HandlerInterceptor {
    boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler);
    void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView);
    void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex);
}

// 自定义拦截器
@Component
public class AuthInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        String token = request.getHeader("Authorization");
        if (token != null) {
            return true;  // 继续执行
        }
        response.setStatus(401);
        return false;  // 中断执行
    }
}

// 配置拦截器链
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    private AuthInterceptor authInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(authInterceptor)
                .addPathPatterns("/**")
                .excludePathPatterns("/login");
    }
}

练习题

  1. 责任链模式和装饰器模式的区别?
  2. 如何避免责任链过长?
  3. 责任链模式如何保证请求一定被处理?
  4. Servlet Filter和Spring Interceptor的区别?
  5. 如何实现可配置的责任链?

Released under the MIT License.