设计模式之责任链模式

责任链模式:构建灵活的请求处理流程

  • 一、责任链模式简介
    • 1.1 概念
    • 1.2 模式类型
    • 1.3 优点
    • 1.4 缺点
  • 二、责任链模式的模式动机
  • 三、模式结构
  • 四、责任链模式的实现
    • 4.1 需求
    • 4.2 实现步骤
    • 4.3 具体实现
      • 1. 用户账户类
      • 2. 校验器父类
      • 3. 校验器子类
      • 4. 客户端测试
      • 5. 运行结果
  • 五、责任链模式的应用场景
    • 5.1 适用场景
    • 5.2 应用实例

一、责任链模式简介

1.1 概念


   责任链模式(Chain of Responsibility Pattern) 是一种常用的设计模式,属于行为型模式的一种。在责任链模式中,多个对象都有机会处理请求,从而形成一条链:请求从一个对象传递到下一个对象,以此类推。这种模式允许请求的发送者和接收者解耦。在责任链模式中,请求可以被多个对象处理,知道其中一个对象负责处理它为止。

1.2 模式类型


   行为型设计模式

1.3 优点


  1. 解耦:请求的发送者和接收者解耦,发送者不需要知道请求的处理者是谁,只需要将请求发送给链中的第一个处理者。
  2. 动态组合:责任链可以动态组合,使用配置设置责任链的顺序及是否出现;可以随时对责任链排序,随时增加拆除责任链中的某个请求对象。
  3. 单一职责原则:责任链模式符合单一职责原则,每个处理者只负责处理自己的业务逻辑,使得系统更容易扩展和维护。
  4. 满足开闭原则: 新增或删除处理者时,无需修改现有代码,满足开闭原则。

1.4 缺点


  1. 性能: 由于责任链模式中的请求在链条上依次传递,如果责任链过长或者处理者处理能力不足,可能导致请求处理时间过长,影响系统性能。
  2. 请求处理不保证被接收: 责任链模式中,某个请求可能因为未被任何处理者处理而被忽略,这可能导致系统无法处理某些请求。
  3. 调试困难: 责任链模式中由多个处理者功能处理请求,对于一个请求的处理过程和结果跟踪可能较为困难,尤其当责任链较长或者处理者较多时。
  4. 可能造成循环调用: 在设计责任链模式时,如果处理者之间的联系设置不当,有可能造成循环调用,导致系统陷入死循环。
    设计模式之责任链模式_第1张图片

二、责任链模式的模式动机


   在软件开发过程中,一个请求可能需要多个不同的处理对象。传统的处理方式是,用一个巨大的条件分支语句(if-elseswitch)来决定每个请求的处理者。这种方式的问题在于,处理逻辑与处理对象耦合度高,违反了开放封闭原则,同时使得系统难以维护和扩展。

   责任链模式提供了一种更优雅的方式来解决这个问题。通过构建一个链式的处理对象集合,每个处理对象只关注自己责任范围内的请求,对于不属于自己处理范围的请求,就传递给链上的下一个对象处理。这样,可以将请求的发送者和接收者进行有效解耦,提高系统的灵活性和可扩展性。

三、模式结构


   责任链模式主要包含以下两个核心组成部分:

  1. 处理者Handler):定义一个处理请求的接口,包含处理请求的方法和一个指向链上下一个处理者的引用。
  2. 具体处理者Concrete Handler):实现处理者接口,处理它负责的请求,可以访问它的继任者。如果可处理该请求,就处理,否则将该请求转发给它的继任者。
    设计模式之责任链模式_第2张图片

四、责任链模式的实现

4.1 需求


   假设有一个需求:账号注册
   账号注册时进行校验,先校验账号、再校验密码,最后校验手机号等。

校验规则:

  1. 校验用户名,用户名不能为空,长度不能超过20个字符
  2. 校验密码,密码不能为空,长度不能超过16个字符,必须包含数字和字母
  3. 校验手机号,手机号不能为空,必须时11位数字

4.2 实现步骤


  1. 创建三个校验类,UsernameVerifyPasswordVerifyPhoneNumberVerify
  2. 实现 Verify 接口,并设置 nextVerify 属性,用于设置下一个校验类
  3. 创建 UserAccount 类,用于保存用户输入的账号信息
  4. 创建责任链,并设置第一个校验类为 usernameVerify,并设置其 nextVerify 属性为 passwordVerify
  5. 调用 deploy 方法,传入 UserAccount 实例,开始验证
  6. 验证失败,则返回错误信息,否则返回成功信息

4.3 具体实现

1. 用户账户类


public class UserAccount {

    private String username;
    private String password;
    private String phoneNumber;

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getPhoneNumber() {
        return phoneNumber;
    }

    public void setPhoneNumber(String phoneNumber) {
        this.phoneNumber = phoneNumber;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

}

2. 校验器父类


public abstract class Verify {

    protected Verify verify;

    public void setNextVerify(Verify verify){
        this.verify = verify;
    }

    public abstract void deploy(UserAccount userAccount);

}

3. 校验器子类


  1. 用户名校验器
    public class UsernameVerify extends Verify {
        @Override
        public void deploy(UserAccount userAccount) {
            if (StringUtils.isNotEmpty(userAccount.getUsername()) && userAccount.getUsername().length() <= 20) {
                System.out.println("用户名验证通过");
    
                // 获取下一个验证器
                if (verify != null) {
                    verify.deploy(userAccount);
                }
            } else {
                System.out.println("用户名不能为空,长度不能超过20个字符!");
            }
        }
    }
    
    
  2. 密码校验器
    public class PasswordVerify extends Verify {
        @Override
        public void deploy(UserAccount userAccount) {
            if (checkPassword(userAccount.getPassword())) {
                System.out.println("密码校验通过");
    
                // 获取下一个验证器
                if (verify != null) {
                    verify.deploy(userAccount);
                }
            } else {
                System.out.println("密码不能为空,长度不能超过16个字符,必须包含数字和字母");
            }
        }
    
        public boolean checkPassword(String password) {
            if (StringUtils.isEmpty(password)) {
                return false;
            }
            if (password.length() > 16) {
                return false;
            }
            return password.matches(".*[0-9].*") && password.matches(".*[a-zA-Z].*");
        }
    }
    
  3. 电话号码校验器
    public class PhoneNumberVerify extends Verify {
        @Override
        public void deploy(UserAccount userAccount) {
            if (checkPhoneNumber(userAccount.getPhoneNumber())) {
                System.out.println("手机号码验证通过");
    
                if (verify != null) {
                    verify.deploy(userAccount);
                }
            } else {
                System.out.println("校验手机号,手机号不能为空,必须是11位数字");
            }
        }
    
        public boolean checkPhoneNumber(String phoneNumber) {
            if (StringUtils.isEmpty(phoneNumber)) {
                return false;
            }
            if (phoneNumber.length() != 11) {
                return false;
            }
            for (int i = 0; i < 11; i++) {
                if (!Character.isDigit(phoneNumber.charAt(i))) {
                    return false;
                }
            }
            return true;
        }
    }
    

4. 客户端测试


public class Client {
    public static void main(String[] args) {
        UsernameVerify usernameVerify = new UsernameVerify();
        PasswordVerify passwordVerify = new PasswordVerify();
        PhoneNumberVerify phoneNumberVerify = new PhoneNumberVerify();


        UserAccount userAccount = new UserAccount();
        userAccount.setUsername("qinj");
        userAccount.setPassword("123456abc");
        userAccount.setPhoneNumber("13800138000");

        // 创建职责链
        usernameVerify.setNextVerify(passwordVerify);
        passwordVerify.setNextVerify(phoneNumberVerify);

        // 开始验证
        usernameVerify.deploy(userAccount);
    }
}

5. 运行结果


设计模式之责任链模式_第3张图片

五、责任链模式的应用场景

5.1 适用场景


  • 多个对象可以处理一个请求,但具体由哪个对象处理该请求在运行时自动决定,可以使用责任链模式。
  • 可由客户端动态指定一组对象处理请求,或添加新的处理者,可以使用责任链模式。
  • 所需处理者及其顺序必须在运行时进行改变,可以使用责任链模式。

5.2 应用实例


  • 用户认证和授权
  • 日志记录和异常处理
  • 生活中各种审批链(请假、采购)
  • 各种客服的专线(人工客服按指定数字转接)
  • JS 中的事件冒泡
  • Tomcat 对 Encoding` 的处理
  • Java 的拦截器链
  • Servlet 的过滤器链

你可能感兴趣的:(设计模式,设计模式,责任链模式,java)