设计模式-责任链模式

写在前面

Hello,我是易元,这篇文章是我学习设计模式时的笔记和心得体会。如果其中有错误,欢迎大家留言指正!

一、初始阶段的直筒式实现

项目初期,登录校验通常只需用户名和密码验证,通常采用硬编码方式完成基础验证:

public boolean validateLogin(String username, String password) {
    if (username == null || username.length() < 5) {
        System.out.println("用户名长度不足");
        return false;
    }
    if (!password.matches("^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)[A-Za-z\\d]{8,}$")) {
        System.out.println("密码强度不足");
        return false;
    }
    return "admin".equals(username) && "Secret123".equals(password);
}

但随着用户量增加,安全需求不断升级:

  • 验证码校验:防止脚本攻击

  • IP黑名单拦截:拦截高频攻击IP

  • 设备指纹校验:增强场景化风控

每次新增需求都需在原有方法中插入if判断,代码逐渐臃肿,演变成难以维护的“瑞士军刀式”代码。

二、原始代码的痛点

1. 痛点分析

  • 牵一发而动全身:新增校验逻辑需修改核心方法,易引发连锁BUG。

  • 流程僵化:校验顺序硬编码,无法动态调整。

  • 可读性差:新人接手时难以理解逻辑编排的合理性。

2. 违反的设计原则

传统实现方式演变为:

public boolean validateEverything(...) {
    // 十几个校验逻辑挤在一起
}

已明显违反:

  • 开闭原则:扩展需修改已有代码,而非通过新增类实现。

  • 单一职责原则:一个方法承担过多校验职责。

  • 迪米特法则:模块间耦合度过高。

三、责任链模式重构实战

1. 定义处理流水线

将校验逻辑拆分为独立处理器,形成“质检流水线”,每个节点专注单一职责,失败则中断流程,成功则传递至下一节点。

抽象处理器模板
public abstract class Handler {
    private Handler next;
    
    public Handler linkWith(Handler next) {
        this.next = next;
        return next;
    }
    
    public abstract boolean check(LoginRequest request);
    
    protected boolean proceed(LoginRequest request) {
        return next == null || next.check(request);
    }
}
具体实现示例:IP黑名单校验
public class IPHandler extends Handler {
    @Override
    public boolean check(LoginRequest request) {
        if (isBlacklisted(request.getIp())) {
            log.warn("拦截可疑IP: {}", request.getIp());
            return false;
        }
        return proceed(request);
    }
    
    private boolean isBlacklisted(String ip) {
        // 查询IP黑名单库
    }
}

2. 封装请求上下文

将零散参数封装为对象,统一传递上下文:

public class LoginRequest {
    private String username;
    private String password;
    private String captcha;
    private String ip;
    // 其他参数...
}

3. 动态组装执行链

按需组合处理器,灵活控制校验顺序:

Handler chain = new UsernameHandler()
    .linkWith(new CaptchaHandler())
    .linkWith(new IPHandler())
    .linkWith(new PasswordHandler());

测试用例

@Test
void testRiskLogin() {
    LoginRequest request = new LoginRequest(
        "admin", "WrongPass123", "captcha", "192.168.10.100"
    );
    boolean result = chain.check(request);
    assertFalse(result);
}
执行结果
[校验] 用户名格式合法  
[校验] 验证码校验通过  
[风控拦截] 高风险IP  
登录结果:失败  

四、Spring Security的启示

Spring Security的过滤器链是责任链模式的经典实现

核心过滤器链逻辑(简化)
public class FilterChainProxy implements Filter {
    private List chains;

    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) {
        for (SecurityFilterChain f : chains) {
            f.doFilter(req, res, chain); // 责任链传递
        }
    }
}
具体过滤器实现
public class UsernamePasswordAuthenticationFilter implements Filter {
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) {
        // 认证逻辑处理
        chain.doFilter(req, res); // 传递到下一过滤器
    }
}

模式映射

  • HandlerFilter 接口

  • linkWith() → Spring通过List维护过滤器顺序

  • check()doFilter() 方法

五、长话短说

1. 适用场景

  • 需要动态组合处理流程时

  • 存在多个候选处理逻辑时

  • 需解耦请求方与处理方时

2. 实现四步法

  1. 定义抽象接口:约定处理与链式传递机制。

  2. 实现处理节点:每个类专注单一校验逻辑。

  3. 组装执行链:按需组合处理器。

  4. 封装上下文:统一传递参数。

3. 避坑指南

  • 避免长链:节点超过10个时建议拆分。

  • 性能敏感场景慎用:链式调用存在轻微性能损耗。

  • 统一异常处理:明确中断、继续等策略。

  • 防循环引用:链配置需严格检测。

4. 扩展技巧

  • 结合策略模式:动态替换处理器实现。

  • 添加监控节点:记录各节点执行耗时。

  • 优先级控制:通过@Order注解调整顺序。

你可能感兴趣的:(java,网络,开发语言)