责任链设计模式

2021-01-28 设计模式架构设计

允许你将请求沿着处理者链进行发送。 收到请求后, 每个处理者均可对请求进行处理, 或将其传递给链上的下个处理者。

# 流程梳理

  • 客户端发出一个请求,链上的对象都有机会来处理这一请求,而客户端不需要知道谁是具体的处理对象
  • 让多个对象都有机会处理请求,避免请求的发送者和接收者之间的耦合关系,将这个对象连成一条调用链,并沿着这条链传递该请求,直到有一个对象处理它才终止
  • 有两个核心行为:一是处理请求,二是将请求传递到下一节点

# 应用刨析

假如你正在开发一个在线订购系统。 你希望对系统访问进行限制, 只允许认证用户创建订单。 此外, 拥有管理权限的用户也拥有所有订单的完全访问权限。简单规划后, 你会意识到这些检查必须依次进行。 只要接收到包含用户凭据的请求, 应用程序就可尝试对进入系统的用户进行认证。 但如果由于用户凭据不正确而导致认证失败, 那就没有必要进行后续检查了。

责任链解决的问题

请求必须经过一系列检查后才能由订购系统来处理。在接下来的几个月里, 你实现了后续的几个检查步骤。

  • 一位同事认为直接将原始数据传递给订购系统存在安全隐患。 因此你新增了额外的验证步骤来清理请求中的数据。
  • 过了一段时间, 有人注意到系统无法抵御暴力密码破解方式的攻击。 为了防范这种情况, 你立刻添加了一个检查步骤来过滤来自同一 IP 地址的重复错误请求。
  • 又有人提议你可以对包含同样数据的重复请求返回缓存中的结果, 从而提高系统响应速度。 因此, 你新增了一个检查步骤, 确保只有没有满足条件的缓存结果时请求才能通过并被发送给系统。

每增加一个检查步骤,程序都变得更加臃肿、混乱和丑陋

**代码变得越来越多, 也越来越混乱。**检查代码本来就已经混乱不堪, 而每次新增功能都会使其更加臃肿。 修改某个检查步骤有时会影响其他的检查步骤。 最糟糕的是, 当你希望复用这些检查步骤来保护其他系统组件时, 你只能复制部分代码, 因为这些组件只需部分而非全部的检查步骤。

系统会变得让人非常费解, 而且其维护成本也会激增。 你在艰难地和这些代码共处一段时间后, 有一天终于决定对整个系统进行重构。

# 应用场景

  • Apache Tomcat 对 Encoding 编码处理的处理,SpringBoot 里面的拦截器、过滤器链
  • 在请求处理者不明确的情况下向多个对象中的一个提交请求
  • 如果有多个对象可以处理同一个请求,但是具体由哪个对象处理是由运行时刻动态决定的,这种对象就可以使用职责链模式

# 需求分析

风控规则,就是对于每个场景,定义一些规则,来进行相应的控制,比如银行借款、支付宝提现、大额转账等 会触发不同的策略。像互联网金融行业,除了公司内部政策,所处的外部环境经常发生变化,比如国家经常会出政策,这些都经常需要调整相应的风控参数和风控级别。

例子:支付宝转账,根据转账额度不同,会触发的风控级别不一样,1000元以下直接转,1千到1万需要手机号验证码,1万到以上需要刷脸验证。

责任链设计模式

# 编码实现

创建一个请求类

@Data
public class Request {

    /**
     * 类别
     */
    private String requestType;

    /**
     * 金额
     */
    private int money;

}

创建一个枚举用于记录状态

public enum RequestType {

    /**
     * 转账
     */
    TRANSFER,

    /**
     * 提现
     */
    CASH_OUT
}

创建一个风控管理类

public abstract class RiskControlManager {


    protected String name;

    public RiskControlManager(String name) {
        this.name = name;
    }

    /**
     * 更严格的风控策略
     */
    protected RiskControlManager superior;

    /**
     * 设置更严格的风控策略
     */
    public void setSuperior(RiskControlManager superior){
        this.superior = superior;
    }

    /**
     * 处理请求
     * @param request
     */
    public abstract void handlerRequest(Request request);
}

创建具体风控实现类,初级风控

public class FirstRiskControl extends RiskControlManager{

    public FirstRiskControl(String name) {
        super(name);
    }

    @Override
    public void handlerRequest(Request request) {
        if (RequestType.valueOf(request.getRequestType())!=null && request.getMoney()<=1000){
            System.out.println("普通操作输入支付密码即可");
            System.out.println(name+":"+request.getRequestType()+",金额:"+request.getMoney());
        }else {
            // 下一个节点进行处理
            if (superior!=null){
                superior.handlerRequest(request);
            }
        }
    }
}

中级风控

public class SecondRiskControl extends RiskControlManager{

    public SecondRiskControl(String name) {
        super(name);
    }

    @Override
    public void handlerRequest(Request request) {
        if (RequestType.valueOf(request.getRequestType())!=null && request.getMoney()>1000 && request.getMoney()<10000){
            System.out.println("中等风控,输入支付密码+短信验证码");
            System.out.println(name+":"+request.getRequestType()+",金额:"+request.getMoney());
        }else {
            // 下一个节点进行处理
            if (superior!=null){
                superior.handlerRequest(request);
            }
        }
    }
}

高级风控

    public class ThirdRiskControl extends RiskControlManager{

        public ThirdRiskControl(String name) {
            super(name);
        }

        @Override
        public void handlerRequest(Request request) {
            if (RequestType.valueOf(request.getRequestType())!=null && request.getMoney()>=10000){
                System.out.println("中等风控,输入支付密码+短信验证码+人脸识别");
                System.out.println(name+":"+request.getRequestType()+",金额:"+request.getMoney());
            }else {
                // 下一个节点进行处理
                if (superior!=null){
                    superior.handlerRequest(request);
                }
            }
        }
    }

使用

public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);
    System.out.println("请输入金额");
    int money = sc.nextInt();


    RiskControlManager firstControlManager = new FirstRiskControl("初级风控");
    RiskControlManager secondControlManager = new SecondRiskControl("中级风控");
    RiskControlManager thirdControlManager = new ThirdRiskControl("高级风控");


    // 形成调用链
    firstControlManager.setSuperior(secondControlManager);
    secondControlManager.setSuperior(thirdControlManager);

    Request request1 =  new Request();
    request1.setRequestType(RequestType.CASH_OUT.name());
    request1.setMoney(money);
    firstControlManager.handlerRequest(request1);
}

# 小结

优点

  • 客户只需要将请求发送到职责链上即可,无须关心请求的处理细节和请求的传递,所以职责链将请求的发送者和请求的处理者 降低了耦合度
  • 通过改变链内的调动它们的次序,允许动态地新增或者删除处理类,比较很方便维护
  • 增强了系统的可扩展性,可以根据需要增加新的请求处理类,满足开闭原则
  • 每个类只需要处理自己该处理的工作,明确各类的责任范围,满足单一职责原则

缺点

  • 处理都分散到了单独的职责对象中,每个对象功能单一,要把整个流程处理完,需要很多的职责对象,会产生大量的细粒度职责对象
  • 不能保证请求一定被接收;
  • 如果链路比较长,系统性能将受到一定影响,而且在进行代码调试时不太方便
上次更新: 5 个月前