• 设计模式之责任链模式


    阅读建议

    嗨,伙计!刷到这篇文章咱们就是有缘人,在阅读这篇文章前我有一些建议:

    1. 本篇文章大概4000多字,阅读时间长可能需要4-5分钟;
    2. 设计模式属于程序的设计思想、方法类的内容,阅读一遍,在理解上不一定会很透彻,建议收藏起来,有空多看看,书读百遍,其义自现;
    3. 创作不易,免费的点赞、关注,请走上一走,也算是对博主一些鼓励,可以让我更有动力输出更多的干货内容;

    什么是责任链模式

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

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

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

    责任链模式如何实现?

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

    需求描述

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

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

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

    实现方法

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

    1. /**
    2. * 抽象的分步组装处理类
    3. */
    4. public abstract class AbstractHandler {
    5. private AbstractHandler abstractHandler;
    6. public AbstractHandler getAbstractHandler() {
    7. return abstractHandler;
    8. }
    9. public void next(AbstractHandler abstractHandler) {
    10. this.abstractHandler = abstractHandler;
    11. }
    12. public abstract void make();
    13. }

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

    1. /**
    2. * 主板安装
    3. */
    4. public class MotherboardHandler extends AbstractHandler {
    5. @Override
    6. public void make() {
    7. System.out.println("主机安装完成");
    8. if (this.getAbstractHandler() != null) {
    9. this.getAbstractHandler().make();
    10. }
    11. }
    12. }

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

    1. /**
    2. * 电池安装
    3. */
    4. public class BatteryHandler extends AbstractHandler{
    5. @Override
    6. public void make() {
    7. System.out.println("电池组装完成");
    8. if (this.getAbstractHandler() != null) {
    9. this.getAbstractHandler().make();
    10. }
    11. }
    12. }

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

    1. /**
    2. * 屏幕安装
    3. */
    4. public class ScreenHandler extends AbstractHandler{
    5. @Override
    6. public void make() {
    7. System.out.println("屏幕安装完成");
    8. if (this.getAbstractHandler() != null) {
    9. this.getAbstractHandler().make();
    10. }
    11. }
    12. }

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

    1. /**
    2. * 上电测试
    3. */
    4. public class TestHandler extends AbstractHandler {
    5. @Override
    6. public void make() {
    7. System.out.println("上电测试通过");
    8. if (this.getAbstractHandler() != null) {
    9. this.getAbstractHandler().make();
    10. }
    11. }
    12. }

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

    1. /**
    2. * 生产线
    3. */
    4. public class ProductLine {
    5. public static void main(String[] args) {
    6. AbstractHandler step1=new MotherboardHandler();
    7. AbstractHandler step2=new BatteryHandler();
    8. AbstractHandler step3=new ScreenHandler();
    9. AbstractHandler step4=new TestHandler();
    10. step1.next(step2);
    11. step2.next(step3);
    12. step3.next(step4);
    13. step1.make();
    14. }
    15. }

    如何扩展

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

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

    1. /**
    2. * 报像头安装
    3. */
    4. public class CameraHandler extends AbstractHandler{
    5. @Override
    6. public void make() {
    7. System.out.println("摄像头安装完成");
    8. if (this.getAbstractHandler() != null) {
    9. this.getAbstractHandler().make();
    10. }
    11. }
    12. }

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

    1. /**
    2. * 生产线
    3. */
    4. public class ProductLine {
    5. public static void main(String[] args) {
    6. AbstractHandler step1=new MotherboardHandler();
    7. AbstractHandler step1_2=new CameraHandler();
    8. AbstractHandler step2=new BatteryHandler();
    9. AbstractHandler step3=new ScreenHandler();
    10. AbstractHandler step4=new TestHandler();
    11. step1.next(step2);
    12. step1.next(step1_2);
    13. step1_2.next(step2);
    14. step2.next(step3);
    15. step3.next(step4);
    16. step1.make();
    17. }
    18. }

    责任链模式的适用场景

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

    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()方法中判断是否已经是超过其阈值,超过则不允许建立,避免无意识的破坏系统性能。

  • 相关阅读:
    仿黑马点评-redis整合【二——商户查询缓存】——缓存穿透、缓存击穿的解决
    web前端期末大作业 HTML+CSS+JavaScript仿安踏
    Integer超出-128——127范围的数值比较为什么要用equals
    MySQL数据库————数据库与vs的连接
    IP地址技术原理
    【人工智能Ⅰ】8-回归 & 降维
    java计算机毕业设计-医院门诊分诊信息系统-源程序+mysql+系统+lw文档+远程调试
    Python爬虫实战(基础篇)—13获取《人民网》【最新】【国内】【国际】写入Word(附完整代码)
    【嵌入式Linux】编译应用和ko内核模块Makefile使用记录
    【继承顺序和方式,子类构造方法,protected 关键字,final 关键字】
  • 原文地址:https://blog.csdn.net/fox9916/article/details/134220129