设计模式之责任链模式

什么是责任链模式?

在一些业务场景下,为了避免请求发送者与多个请求处理者耦合在一起,于是将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。在责任链模式中,多个处理器(也就是刚刚定义中说的"接收对象")依次处理同一个请求。一个请求先经过A处理器处理,然后再把请求传递给B处理器,B处理器处理完后再传递给C处理器,以此类推,形成一个链条。链条上的每个处理器各自承担各自的处理职责,所以叫作职责链模式。

责任链模式的核心角色有哪些?

  1. 抽象处理者(Handler):定义一个处理请求的接口,通常包含一个指向下一个处理者的引用。
  2. 具体处理者(ConcreteHandler):实现抽象处理者接口,负责处理它所能处理的请求,如果自己不能处理,就将请求传递给下一个处理者。
  3. 客户类(Client)角色:创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。

责任链模式如何实现?

这里以工厂的流水线生产为例,来分析研究一下责任链模式如何实现。

需求描述

假设一台手机的各种零部件已经制造完成,具体的手机的组装过程是这样的:

  1. 主板安装
  2. 电池安装
  3. 屏幕安装
  4. 通电测试

如果写一段程序来模拟这个组装过程,选择使用责任链模式来实现应该怎么实现呢?

实现方法

1、声明一个抽象的分步组装的处理类,定义抽象的组装方法和指向下一步的处理者的引用 ;

java 复制代码
/**
 * 抽象的分步组装处理类
 */
public abstract class AbstractHandler {
    private AbstractHandler abstractHandler;
    public AbstractHandler getAbstractHandler() {
        return abstractHandler;
    }
    public void next(AbstractHandler abstractHandler) {
        this.abstractHandler = abstractHandler;
    }
    public abstract void make();
}

2、声明具体的处理者,即主板安装的处理者,继承了抽象的分步组装处理器,实现抽象的组装方法,即主板的安装;

java 复制代码
/**
 * 主板安装
 */
public class MotherboardHandler extends AbstractHandler {
    @Override
    public void make() {
        System.out.println("主机安装完成");
        if (this.getAbstractHandler() != null) {
            this.getAbstractHandler().make();
        }
    }
}

3、声明第二具体的处理者,即电池安装的处理者,继承了抽象的分步组装处理器,实现抽象的组装方法,即电池的安装;

java 复制代码
/**
 * 电池安装
 */
public class BatteryHandler extends AbstractHandler{
    @Override
    public void make() {
        System.out.println("电池组装完成");
        if (this.getAbstractHandler() != null) {
            this.getAbstractHandler().make();
        }
    }
}

4、声明具体的处理者,即屏幕安装的处理者,继承了抽象的分步组装处理器,实现抽象的组装方法,即屏幕的安装;

java 复制代码
/**
 * 屏幕安装
 */
public class ScreenHandler extends AbstractHandler{
    @Override
    public void make() {
        System.out.println("屏幕安装完成");
        if (this.getAbstractHandler() != null) {
            this.getAbstractHandler().make();
        }
    }
}

5、声明具体的处理者,即整机上电测试的处理者,继承了抽象的分步组装处理器,实现抽象的组装方法,即上电测试;

java 复制代码
/**
 * 上电测试
 */
public class TestHandler extends AbstractHandler {
    @Override
    public void make() {
        System.out.println("上电测试通过");
        if (this.getAbstractHandler() != null) {
            this.getAbstractHandler().make();
        }
    }
}

6、声明一个客户端,也就是手机组装生产线,主要业务也就是把组装过程的每一步整合起来,这样一条完整的手机组装生产线就搭建好了,可以开始生产活动了;

java 复制代码
/**
 * 生产线
 */
public class ProductLine {
    public static void main(String[] args) {
        AbstractHandler step1=new MotherboardHandler();
        AbstractHandler step2=new BatteryHandler();
        AbstractHandler step3=new ScreenHandler();
        AbstractHandler step4=new TestHandler();
        step1.next(step2);
        step2.next(step3);
        step3.next(step4);
        step1.make();
    }
}

如何扩展

科技进步了,手机不仅能接电话和打电话,还能拍照,那么原来的手机组装生产线就需要升级,增加摄像头的组装。面对这样的扩展需求,应该怎么实现呢?很简单,往下看:

1、增加一个工位,即声明一个摄像头安装的处理类,同样继承于抽象的分步组装处理器,实现抽象的组装方法,即摄像头安装;

java 复制代码
/**
 * 报像头安装
 */
public class CameraHandler extends AbstractHandler{
    @Override
    public void make() {
        System.out.println("摄像头安装完成");
        if (this.getAbstractHandler() != null) {
            this.getAbstractHandler().make();
        }
    }
}

2、原来的生产线的工作顺序调整一下,在主机安装完成后,增加一步摄像头的组装,就可以了;原来的其他步骤的工作逻辑不变,完全符合开闭原则。

java 复制代码
/**
 * 生产线
 */
public class ProductLine {
    public static void main(String[] args) {
        AbstractHandler step1=new MotherboardHandler();
        AbstractHandler step1_2=new CameraHandler();
        AbstractHandler step2=new BatteryHandler();
        AbstractHandler step3=new ScreenHandler();
        AbstractHandler step4=new TestHandler();
        step1.next(step2);
        step1.next(step1_2);
        step1_2.next(step2);
        step2.next(step3);
        step3.next(step4);
        step1.make();
    }
}

责任链模式的适用场景

责任链模式适用于处理具有依次顺序、多个步骤的场景,如:

  1. 订单处理系统:在订单处理系统中,订单需要依次经过多个环节的处理,比如校验订单信息、生成订单号、计算订单金额、库存扣减等等。每个环节都有可能出现异常情况,需要进行相应的处理。使用责任链模式,可以将每个处理环节封装成一个处理器,通过责任链将订单依次传递给这些处理器进行处理,直到订单处理完成。这样可以使得订单处理流程更加清晰和灵活。
  2. 日志记录系统:在一个日志记录系统中,日志需要经过多个过滤器进行处理,比如按照日志级别进行过滤、按照关键字进行过滤、按照时间进行过滤等等。每个过滤器都有自己的处理逻辑和优先级,需要根据配置来确定日志经过哪些过滤器进行处理。使用责任链模式,可以将每个过滤器封装成一个处理器,通过责任链将日志依次传递给这些过滤器进行处理,直到所有过滤器都处理完毕。这样可以使得日志过滤流程更加灵活和可配置。
  3. 用户登录验证:手机验证码登录需要进行用户是否存在、验证码是否为空、验证码是否正确、用户是否锁定、用户是否被禁用等校验。这些校验都可以封装成处理器,通过责任链依次传递给这些处理器进行处理。

责任链模式在Spring中的应用

责任链模式在Spring中有很多应用场景:

  1. Spring的异常处理:Spring的异常处理机制采用了责任链模式,通过定义不同的异常处理器,将不同的异常类型传递给不同的处理器进行处理。这样可以使得异常处理更加灵活和可配置。
  2. Spring的拦截器:Spring的拦截器使用了责任链模式,通过定义不同的拦截器,将请求依次传递给这些拦截器进行处理。这样可以使得请求处理流程更加清晰和灵活。
  3. Spring的AOP实现:Spring的AOP实现也使用了责任链模式,通过定义不同的切面(Aspect),将请求依次传递给这些切面进行处理。这样可以使得代码更加清晰和易于维护。
  4. Spring的HandlerInterceptor:在Spring MVC框架中,可以使用HandlerInterceptor来拦截请求并处理请求。每个HandlerInterceptor都可以看作是一个处理器,通过责任链将请求依次传递给这些处理器进行处理。这样可以使得请求处理流程更加灵活和可配置。

总结

优点

  1. 降低对象之间的耦合度:责任链模式使得一个对象无须知道到底是哪一个对象处理其请求以及链的结构,发送者和接收者也无须拥有对方的明确信息。
  2. 增强系统的可扩展性:可以根据需要增加新的请求处理类,满足开闭原则。
  3. 增强给对象指派职责的灵活性:当工作流程发生变化时,可以动态地改变链内的成员或者调动它们的次序,也可动态地新增或者删除责任。
  4. 简化对象之间的连接:每个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的if或者if···else语句。
  5. 责任分担:每个类只需要处理自己该处理的工作,不该处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。

缺点

  1. 性能问题:每个请求都是从链表头遍历到链表尾,特别是链表比较长的时候,性能是一个非常大的问题。
  2. 调试不方便:特别是链表比较长,环节比较多的时候,由于采用了类似递归的方式,调试的时候可能逻辑比较复杂。

在使用责任链模式时,需要注意链中节点的数量需要控制,避免出现超长链的情况。一般做法是在Handler中设置一个最大的节点数量,在setNext()方法中判断是否已经是超过其阈值,超过则不允许建立,避免无意识的破坏系统性能。

相关推荐
c++之路4 分钟前
C++23概述
java·c++·c++23
专注API从业者1 小时前
Open Claw 京东商品监控选品实战:一键抓取、实时监控、高效选品
java·服务器·数据库
摇滚侠1 小时前
DBeaver 导入数据库 导入 SQL 文件 MySQL 备份恢复
java·数据库·mysql
keep one's resolveY2 小时前
SpringBoot实现重试机制的四种方案
java·spring boot·后端
天空属于哈夫克32 小时前
企业微信API常见的错误和解决方案
java·数据库·企业微信
摇滚侠3 小时前
VMvare 虚拟机 Oracle19c 安装步骤,远程连接 Oracle19c,百度网盘安装包
java·oracle
梁萌3 小时前
idea报错找不到XX包的解决方法
java·intellij-idea·启动报错·缺少包
Agent产品评测局3 小时前
生产排期与MES/ERP系统打通,实操方法详解 —— 2026企业级智能体自动化选型与实战指南
java·运维·人工智能·ai·chatgpt·自动化
阿丰资源3 小时前
基于Spring Boot的电影城管理系统(直接运行)
java·spring boot·后端
呱牛do it4 小时前
企业级门户网站设计与实现:基于SpringBoot + Vue3的全栈解决方案(Day 8)
java