十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

责任链模式:从嵌套if到优雅的审批链路重构

责任链模式:从嵌套if到优雅的审批链路重构 1. 从一段让我头疼的审批代码说起1.1 需求本来很简单我曾经接手过一个公司内部的OA系统改造里面有一段让我印象极其深刻的需求请假审批。刚拿到需求时感觉不过瘾无非就是“小于3天由组长审批3到7天由部门经理审批大于7天由总经理审批”。可等我打开前任留下的代码时整个人都愣住了。那个年代的代码写得真是“豪放”整个审批逻辑全部堆在一个Service方法里。if分支一个套一个组长判断完了里面套经理判断经理判断完了里面又套总经理判断最后还有个else负责驳回。刚开始还能看懂后面加了“财务复核”、“人事备案”之后方法体直接膨胀到了三百多行。更要命的是一旦出现新的审批角色就得在这三百多行里找到正确的位置小心翼翼地插入一个新分支。这种代码的丑陋之处不仅仅是“长”而已而是每次修改都像在雷区里散步。改组长审批条件时稍不留神就可能影响经理审批的逻辑测试也得从头到尾把所有分支回归一遍。我当时看着这堆嵌套if脑子里冒出一句话这不应该是一串可以自由拼装的链条吗1.2 链式结构与责任链模式后来我重新设计这套审批逻辑的时候做的第一件事就是把这些乱七八糟的if分支彻底拆掉。拆完之后我发现每个审批节点其实都有一个共同的特点判断自己能不能处理这个请求能处理就处理不能处理就把请求往后传。这本质上就是一条“责任链”。责任链模式英文叫Chain of Responsibility是经典GoF设计模式中的一种行为型模式。它的核心思想用一句话说清楚让多个对象都有机会处理同一个请求从而避免请求的发送者和接收者之间的耦合关系。具体做法就是把所有可能处理请求的对象串成一条链请求沿着链依次传递直到链上某个对象把请求处理掉为止。我当时重构审批流程的时候就是这么干的把“组长审批”“经理审批”“总经理审批”各自封装成一个独立的处理器每个处理器手里都握着“下一个处理器”的引用组长的next指向经理经理的next指向总经理。请求进来后先到组长手里组长发现自己处理不了就不声不响地传给经理经理处理不了再传给总经理。这种结构带来的最大好处是增加新的审批节点时不需要改动任何现有代码只要新增一个处理器类然后在组装链的时候把它插进去就行。符合开闭原则对扩展开放、对修改关闭。我当时把这段代码重构完心里最大的感受是原来设计模式书上讲的不是空洞的理论而是专门来解救这种烂代码的。1.3 责任链模式能解决什么问题责任链模式适用的场景我在后来的实践中总结出三个典型特征。第一系统里有多个对象都能处理同一个请求但具体由谁处理在运行前不确定要动态决定。第二你希望请求的发送者和接收者彻底解耦发送方不需要知道到底是谁最终处理了自己的请求。第三你希望可以在运行时动态地调整处理者的组合顺序而不是写死在代码里。用生活化的例子来理解的话想象你在公司里提交了一个IT工单普通问题由一线客服处理解决不了转给二线工程师二线还搞不定就升级到架构组。工单系统在创建工单时根本不需要知道这个问题最终会落到谁头上工单只是沿着“一线→二线→架构组”这条链一路走下去直到有人能解决为止。再比如你的邮件系统一封邮件先经过垃圾邮件过滤器再进病毒扫描器然后是防钓鱼检查最后才到收件箱。每个检查环节都是一个独立的处理器它们串在一起各自负责自己的职责处理完就传给下一个。这跟责任链模式的结构如出一辙。什么场景适合用责任链什么场景不适合我在后面会展开讲。这里先记住一个判断标准如果你的请求处理逻辑还没有完全定型经常要增删节点、调整顺序或者希望把一个巨大的条件判断拆成多个独立类那么责任链模式大概率就是你的菜。2. 责任链模式的核心设计一次讲透2.1 四个角色别搞混责任链模式虽然听上去简单但它的设计角色很容易搞混。我见过不少初学者把“处理器”和“业务逻辑”搅在一起最后写出一个四不像。咱们先把这个模式的基本结构理清楚它会涉及四个角色。第一个是抽象处理者通常叫Handler。它定义了一个处理请求的接口以及一个设置“下一个处理器”的方法。这个角色是整个链条的地基决定了链怎么串、请求怎么传。第二个是具体处理者通常叫ConcreteHandler。它继承或者实现抽象处理者在里面写真正的判断逻辑这个请求我能处理吗能就处理不能就调用next传给下一个。第三个是请求对象通常叫Request。它封装了要传递的数据。在我之前的请假审批例子里Request就是一个包含请假天数和申请人信息的对象。第四个是客户端也就是Client。它负责把多个ConcreteHandler按照顺序串成一条链然后把请求扔给链头。客户端只认识链头不需要知道链上有多少个节点、每个节点做什么。这四个角色里面最容易出问题的是“客户端”。很多初学的人会把组装链的逻辑写在某个Handler里面结果链越来越长、职责越来越乱。正确的做法是组装链是客户端的工作Handler只负责两件事——判断自己能不能处理以及把处理不了的东西传给下一个。2.2 传递规则的两种选择责任链模式在处理请求时一般有两种传递规则。第一种叫作“终止型链”即请求在链上传递直到某一个处理器成功处理了请求链条就停止。这时候被处理过的请求不会再往后传。请假审批就是典型的终止型链经理已经批了就不需要再往上送到总经理那里了。第二种叫作“全遍历型链”即每个处理器都先处理一遍请求然后再传给下一个请求会走完整个链条。日志系统是典型的全遍历型链一条日志先经过控制台输出器再经过文件输出器再经过数据库输出器每个输出器都执行自己的写日志操作日志被所有节点都处理一遍。这两种规则没有谁好谁坏全看业务场景需要。设计抽象处理者时你要想清楚自己的场景到底属于哪一种。如果是终止型链那“处理完就直接return不再调next”如果是全遍历型链那“先处理自己的逻辑再调next往下传”。这里有一个非常值得注意的细节。负责任的老手写责任链时会把“处理请求”和“是否继续传递”这两件事彻底分开以免混淆。我在实际编码中喜欢定义两个方法一个handle方法负责判断并处理另一个next方法专门负责转发。比如下面这样Java代码public abstract class Handler { protected Handler next; public void setNext(Handler next) { this.next next; } // 模板方法确定链条流转逻辑 public final void execute(Request request) { if (canHandle(request)) { doHandle(request); } else if (next ! null) { next.execute(request); } else { System.out.println(当前链上没有处理器能够处理该请求); } } protected abstract boolean canHandle(Request request); protected abstract void doHandle(Request request); }注意我把execute方法声明成final这就是模板方法模式和责任链模式的结合。canHandle判断能不能处理doHandle真正干活execute负责链条流转规则。这样的设计让子类只需要关心“我到底能处理什么”和“我处理的时候做什么”链条怎么走完全不用操心。2.3 链的组装与发起说完角色和传递规则我们来看两件最实际的事链怎么组装请求怎么发起。组装链的方式有好几种。最常见的是在客户端里一个一个setNext代码直观但缺点是链一旦很长客户端代码会显得啰嗦。另一种方式是用建造者模式来封装链的组装比如提供一个Builder类支持链式调用handler.addNext(handler)这样的语法让链的构建更加优雅。还有一种方式是用Spring这类框架的依赖注入来管理配置好顺序由容器自动装配这在企业级应用里特别常见。至于发起请求就更简单了。客户端只需要拿到链头调用它的execute方法剩下的事就交给链自己去流转。这种“发送者不知道接收者是谁”的松耦合设计是责任链模式最值钱的地方。你随时可以改变链的结构增加、减少、重排处理器对客户端代码来说完全透明。我在第一次重构审批系统时把链的组装放在一个专门的ChainConfig类里效果非常好。组长审批后面接经理审批经理审批后面接总经理审批成了什么样子一目了然。后来产品经理又加了一个“副总审批”的环节我只需要新建一个VicePresidentHandler类然后在ChainConfig里把链改成“组长→经理→副总→总经理”客户端一行代码没动新功能就上线了。那一刻我是真切体会到了“模式的力量”这几个字。3. 手写一个责任链请假审批系统的完整实现3.1 先定义请求对象理论说得再多不如直接写代码。我们还是用请假审批这个经典场景完整实现一遍。我用的语言是Java因为Java在企业系统里最常见责任链模式的体现也最清晰。其他面向对象语言照着这个思路也能平移。首先定义一个请求对象也就是请假申请。它需要一个申请人姓名和请假天数这两个字段已经足够支撑后面的审批判断了。代码如下public class LeaveRequest { private String name; private int days; public LeaveRequest(String name, int days) { this.name name; this.days days; } public String getName() { return name; } public int getDays() { return days; } }有些同学可能会问Request对象里要不要放审批结果字段我的建议是如果能不放就不放保持请求对象尽量轻量。审批结果属于处理者的职责范围可以单独用返回值或者出参封装。在简单场景下每个处理器直接打印审批结果就够了。如果业务复杂了可以增加一个审批结果对象但是不要让Request承担过多的职责。3.2 写抽象处理者接下来是核心部分抽象处理者Handler。为了演示更丰富的写法我这次不用模板方法而是把所有逻辑都交给子类自行决定。这种写法更灵活但要求子类严格遵守“处理不了就传递”的规矩。public abstract class Handler { protected Handler next; public void setNext(Handler next) { this.next next; } // 由子类实现具体的处理逻辑 public abstract void handleRequest(LeaveRequest request); // 子类处理不了时调用此方法转发 protected void forward(LeaveRequest request) { if (next ! null) { next.handleRequest(request); } else { System.out.println(没有合适的审批人流程终止请人工介入处理); } } }这里我特意加了一个forward方法目的就是提醒后面写子类的同事处理不了就走forward不要直接拿着next就去调用。虽然功能上两者等价但forward加了“没人处理”时的兜底逻辑日志里一眼就能看出来链条断了是正常终止还是异常问题。这种小细节看着不起眼调试的时候能省不少事。3.3 实现三个具体审批人具体审批人分别是组长、部门经理、总经理。每个审批人做的事情其实就两步先看请假天数在不在自己的权限范围内在就批准不在就往后传。组长审批的逻辑是请假天数小于等于3天代码如下public class GroupLeaderHandler extends Handler { Override public void handleRequest(LeaveRequest request) { if (request.getDays() 3) { System.out.println(组长已批准 request.getName() 请假 request.getDays() 天); } else { forward(request); } } }部门经理审批的逻辑是请假天数大于3天且小于等于7天public class ManagerHandler extends Handler { Override public void handleRequest(LeaveRequest request) { if (request.getDays() 3 request.getDays() 7) { System.out.println(部门经理已批准 request.getName() 请假 request.getDays() 天); } else { forward(request); } } }总经理审批的逻辑是请假天数大于7天public class GeneralManagerHandler extends Handler { Override public void handleRequest(LeaveRequest request) { if (request.getDays() 7) { System.out.println(总经理已批准 request.getName() 请假 request.getDays() 天); } else { forward(request); } } }这三个类的代码结构完全一样以至于有些刚入门的朋友会觉得“这不是把if换了个地方写吗有什么意义”其实意义很大。原来的if写在同一个方法里所有判断逻辑耦合在一起现在每个判断逻辑被封装成独立的类它们之间互不感知只通过next连接。新增审批角色时其他类完全不需要改动。而且每个类的职责单一清晰测试起来也更方便单独测试组长类、单独测试经理类互不干扰。3.4 客户端组装并测试有了请求对象和三个具体处理者之后接下来就是客户端把三个处理者串起来发起一次请假请求。代码非常简洁public class Client { public static void main(String[] args) { // 1. 创建三个处理器 Handler groupLeader new GroupLeaderHandler(); Handler manager new ManagerHandler(); Handler generalManager new GeneralManagerHandler(); // 2. 组装责任链组长 - 经理 - 总经理 groupLeader.setNext(manager); manager.setNext(generalManager); // 3. 发起请假请求 LeaveRequest request1 new LeaveRequest(张三, 2); groupLeader.handleRequest(request1); LeaveRequest request2 new LeaveRequest(李四, 5); groupLeader.handleRequest(request2); LeaveRequest request3 new LeaveRequest(王五, 10); groupLeader.handleRequest(request3); } }运行一下输出结果很符合预期组长已批准 张三 请假 2 天 部门经理已批准 李四 请假 5 天 总经理已批准 王五 请假 10 天这个例子虽然简单但责任链模式的所有要素都已经齐了抽象的Handler、具体的三人、客户端组装、链头发起请求。你可以试着把链的顺序换一下比如先经理后组长看看会发生什么。这样对链的传递机制会更有体感。我当时带新人时最喜欢让他们做这个实验——把链顺序反过来跑一次感受一下责任链模式为什么强调“顺序很重要”。4. 责任链模式不是银弹它的边界在哪4.1 责任链模式 vs 策略模式很多初学者容易把责任链模式和策略模式搞混因为两个模式都是“把算法或处理逻辑封装成独立类”而且都能在运行时切换行为。但它们的核心意图截然不同用错场景会走弯路。策略模式的核心是“让算法的定义和使用分离”。你说你现在有一套价格计算逻辑普通会员一个价VIP会员一个价超级VIP一个价这三个算法互斥运行时会根据条件选中其中一种来执行。这种场景用策略模式就对了客户端只认策略接口具体用哪个策略由调用方或上下文来决定。责任链模式的核心是“让多个处理者有机会处理同一个请求”。它强调的是请求在多个处理者之间流动直到被正确处理或者走完整个链条。从本质上看策略模式是“选一条路走到黑”责任链模式是“走一条路不行就换下一条”。什么时候用哪个我根据自己的踩坑经历总结出一句话如果系统里同时只有一个处理器会生效那优先考虑策略模式如果请求可能被一个处理器处理后还要继续被其他处理器处理或者不确定哪个处理器会接手那就用责任链模式。4.2 责任链模式 vs 装饰器模式另一个容易搞混的是装饰器模式。因为装饰器也是一串对象连在一起每个对象包装下一个对象调用从外到内依次传递最后再逐层返回。表面上看装饰器和责任链都是“链式结构”但两者的出发点差别很大。装饰器模式的核心是“增强功能”每个装饰器都会真正处理这次调用并在处理前后附加逻辑。比如一个IO流外面套一层缓冲流再套一层加密流数据会经过每一层流处理最终到达目的地。这里的“处理”是叠加的所有装饰器都会执行。责任链模式的核心是“职责分担”每个处理器都有机会处理请求但未必都会处理。一旦某个处理器认定自己能处理后续的处理器就不会再执行了。即便你是全遍历型链条每个节点也只是处理自己该处理的部分不存在“包装”和“增强”的关系。所以判断标准也很简单如果你是给某个功能层层叠加新能力用装饰器如果你是让请求在多个人之间流转用责任链。4.3 责任链的典型应用场景我总结了一些在实际项目中真正见过、用过的责任链场景供大家参考。第一个是审批流系统。这是最经典的责任链场景。请假、报销、采购申请等业务通常都要经过多级审批每一级的审批条件、审批权限都不同。用责任链模式把每一级审批做成一个处理器链的顺序就是审批的先后顺序。后续新增审批节点不需要改动已有节点的代码。第二个是数据校验。复杂的表单提交往往要经过多重校验非空校验、格式校验、长度校验、业务逻辑校验。这些校验步骤串成一条责任链请求先走非空校验再走格式校验再走业务校验。一旦某个校验不通过直接终止不再往后走。这样设计的好处是校验逻辑和业务逻辑彻底分离新加一个校验规则也非常方便。第三个是日志系统。经典的日志处理链每条日志从最低级别开始逐级向上传递。DEBUG日志可能只被控制台处理器处理而ERROR日志可能被控制台、文件、邮件多个处理器依次处理。这种全遍历型链条很好地利用了责任链每次请求可能被多个节点处理的特性。第四个是中间件、过滤器、拦截器体系。像Java Servlet里的FilterChainSpring MVC里的InterceptorMyBatis里的Plugin本质上都是责任链模式的变体。一个HTTP请求经过层层过滤器每个过滤器都可以决定“继续放行”还是“拦截下来”。理解了责任链模式再看这些框架的源码会有一种豁然开朗的感觉。4.4 链太长、节点太多怎么办责任链模式有一个隐藏成本就是当链上的处理器特别多的时候请求的传递路径会变得很长。每一层传递都有一次方法调用和一个判断逻辑如果链条有几十个节点每次请求都要沿着链从头走到尾性能上会有一定的损耗。对于绝大多数业务系统来说这个损耗可以忽略不计但如果你的系统对时延极其敏感而且处理器数量确实很多那就要谨慎使用。另外链太长还会带来一个隐性维护成本就是组装链的代码会变得很长。几十个setNext写下来阅读体验很糟糕。我的建议是使用建造者模式来封装链的组装或者把链的组装配置化放到配置文件或者Spring容器里。这样既可以保持责任链模式的灵活性又不会让客户端代码变得没法看。5. 实战中的坑我替你先踩一遍5.1 忘记setNext链断了责任链模式最容易踩的坑就是链断裂。代码写完了自测的时候第一个节点能处理后面的节点怎么都不生效。排查半天发现是在组装链的时候漏了关键的一句groupLeader.setNext(manager)。这个错误看起来低级但真的非常容易出现尤其是当链上的节点比较多时少看一眼就漏了。我在设计抽象Handler时为了减少这个风险会强制在forward方法里做兜底。如果发现next为空且当前节点不能处理就打印一条“链路中断”的日志。这样链条断了能第一时间暴露出来而不是静默失败。另外我强烈建议在单元测试里覆盖“每个节点都能被访问到”的测试用例直接验证链路的完整性。5.2 形成循环链导致死循环另一个我见过的大坑是循环链。有些同学在使用责任链时想做到“请求能回到起点”的效果于是把链的最后一环的next指向了链头。这种设计如果业务上确实需要是没问题的但一定要设置一个跳出机制否则请求就会无限循环下去。比如你的处理器在处理完请求后调用了forward而forward又把请求传给了链头链头再次走一遍逻辑再次调用forward如此反复直到栈溢出。这类bug在测试环境里极具迷惑性因为它不一定每次必现而是取决于请求的具体参数。我的建议是除非有非常明确的业务需求否则不要让责任链形成环。如果确实需要循环处理请在Request里加一个计数器或者最大处理次数限制达到阈值抛异常终止。在handler的forward方法中也加上日志方便排查问题。5.3 处理逻辑和传递逻辑混在一起我还经常看到一些新手把责任链写成了“伪链”。具体表现是每个处理器在处理完自己的逻辑之后没有调用forward传递而是直接又去new下一个处理器的实例然后调用下一个处理器的方法。这样写虽然从输出结果上看和责任链一致但是顺序被写死在了代码里。这种写法带来的问题是灾难性的。一旦要调整顺序就得去改每个处理器的代码。说好的“开闭原则”荡然无存。正确做法是所有处理器之间不应该直接依赖只依赖抽象Handler具体顺序全部由客户端在组装链时决定。这是责任链模式最重要的设计底线怎么强调都不为过。5.4 单例与线程安全问题需要注意如果你在Spring这类容器里使用责任链模式Handler通常会被注册成单例Bean。这意味着多个线程可能同时调用同一个Handler的execute方法。如果Handler内部没有共享的可变状态那没问题但如果Handler里有实例变量用来存储业务数据就要小心线程安全问题了。我的建议是Handler尽量做成无状态的也就是说不要用实例变量保存本次请求的数据。请求数据全部放在Request对象里通过方法参数传递。如果确实需要保存一些上下文信息可以使用ThreadLocal或者把上下文放到Request里。这样既保证了线程安全又让Handler更符合单一职责原则。6. 结合框架源码看责任链你会看得更透6.1 Java Servlet中的FilterChain如果你平时写Java Web应用你一定用过Filter。请求进来先经过过滤器再进行业务处理。这个FilterChain就是责任链模式的一个经典实现。每个Filter持有一个FilterChain引用FilterChain内部维护一个过滤器数组和当前索引。调用chain.doFilter时会从当前索引位置取出对应的Filter然后执行它的doFilter方法等Filter处理完毕后再调用chain.doFilter索引加一继续下一个Filter。这就是为什么你写Filter时如果不在最后调用chain.doFilter请求就会被拦截住。因为过滤器链条没有继续往前走。理解了责任链模式后再看FilterChain的源码你会很自然地明白它的设计意图链上每个节点都有权利决定继续传递还是中断。6.2 Spring MVC中的InterceptorSpring MVC的Interceptor机制也是责任链但它和Servlet Filter有一个区别。Interceptor除了在请求处理前拦截preHandle还有后置处理postHandle和完毕后处理afterCompletion。它相当于在责任链模式的基础上加入了“钩子方法”的概念。这种设计特别适合做登录校验、权限控制、日志审计这类横切逻辑。多个Interceptor串在一起每个都能决定请求是否继续往下走。如果你的项目里有多个拦截器你可以试试把它们替换成责任链模式的写法思路其实是完全一致的。6.3 责任链模式与函数式编程的组合在现代Java中责任链模式还有一种更简洁的写法就是结合函数式接口。每个处理器不再是单独的类而是一个Function或者Consumer对象通过then或者compose组合成链。这种写法特别适合逻辑简单、不想单独建类的小场景可以大幅减少代码量。不过我的建议是如果链上有复杂的业务逻辑还是老老实实拆成独立类。函数式写法虽然简洁但调试不太方便stack trace里全是lambda表达式排查问题比较痛苦。工具类字段少、逻辑少用函数式没问题复杂的业务逻辑还是类比较清晰也方便做单元测试。6.4 C中的责任链实现思路如果你是C选手责任链模式的写法与Java大体一致但需要特别注意资源管理问题。链上的处理器一般都是通过基类指针连接的如果使用裸指针要小心内存泄漏。我见过不少C实现用shared_ptr来管理Handler的引用这样链可以安全地被多个客户端共享。在C中virtual函数实现多态抽象Handler是一个含有纯虚函数的接口每个具体处理器继承并实现handleRequest。组装链和Java一样用setNext把节点串起来客户端把请求扔给链头即可。除了资源管理之外设计思路完全相通。7. 责任链模式的进阶玩法7.1 责任链模板方法让子类更省心我在前面的示例代码中已经把模板方法模式嵌进去了这其实是一种非常强大的组合。模板方法负责定义链条的流转骨架子类只需要实现canHandle和doHandle两个方法不需要去理解和维护next相关的转发逻辑。如果你所在的项目里链条流转规则比较复杂包括“处理完需要记录审计日志”“处理过程中出现异常如何兜底”“是否需要异步传递”等那我强烈建议把流转规则统一封装在抽象处理器的execute方法里。子类写起来干净团队协作时也不容易出错。7.2 责任链建造者链的组装更优雅前面提过链太长时组装代码会很难看。用建造者模式可以很好地解决这个问题。假设我有一个ChainBuilder它提供addHandler和build两个方法你可以像下面这样组装链Handler chain new ChainBuilder() .add(new GroupLeaderHandler()) .add(new ManagerHandler()) .add(new GeneralManagerHandler()) .build();这样链的组装变得非常直观而且可以使用同一个Builder构建出不同顺序的链。我在实际项目中把链的构建配置化配置里写清楚每个节点的BeanName和顺序Spring容器启动时自动装配整个系统的扩展性提升了一个档次。7.3 责任链的事件驱动化改造当责任链模式遇上事件驱动架构时可以玩出更多花样。比如某个业务事件发生后会把事件对象放入责任链链上的每个处理器都对这个事件进行处理有些处理器负责数据校验有些负责触发通知有些负责更新数据库。这样事件的处理逻辑被拆分成了一个个独立的组件新增加一种处理逻辑只是新增加一个节点而已。不过这里要注意事件驱动的责任链通常使用全遍历型链条并且节点的处理顺序可能对结果有影响。消费事件时最好在Request对象里记录处理进度方便异常恢复和重试。这已经属于架构层面的设计考量不是简简单单的模式套用了但在复杂系统中确实能发挥巨大价值。7.4 动态决策责任链运行时调速有些对灵活性要求很高的系统责任链的顺序不是在启动时就定死的而是在运行时根据实际情况动态调整。比如一个风控系统对于不同风险等级的用户采用不同长度的校验链路。风险低的用户走一条短链风险高的用户走一条长链。这种动态责任链要求Handler的注册机制比较灵活比如维护一个Mapkey是业务场景value是链头。请求进来时先根据场景取出对应的链头再将请求扔进去。这种玩法在实战中很有价值但代价是系统的可观测性会变差一定要做好链路的日志追踪不然出问题的时候很难定位到底走到了哪个节点。8. 我的一些实操心得最后分享一点我在实际项目里的体会。责任链模式看起来很简单代码量也不大但它是一个“易学难精”的模式。入门只需要半天能写出正确的代码也不算难但要把这个模式用得恰到好处避免滥用需要踩过不少坑才能真正掌握。我个人最深的感受是责任链模式最大的价值不是“让代码跑起来”而是“让代码能够平稳地应对变化”。当你面对一个频繁调整规则、不断新增处理节点的业务时责任链模式会给你非常舒适的扩展体验。反过来如果你的处理流程已经非常固定短期内没有任何变化需求硬套责任链反而会增加代码的复杂度和阅读成本。设计模式不是装饰品而是解决问题的工具用不用、怎么用取决于你手里的问题到底是什么形状。另外如果你在学习责任链模式时觉得还是有点抽象我建议你先别急着背定义找一个自己熟悉的业务场景比如请假审批、订单校验甚至公司内部的工单流转把代码亲手写一遍。写完之后再试着加一个节点、调整一下链的顺序感受一下这种“改起来很方便”的爽感。亲身体验过之后模式就不再是书上的理论而是长在你自己脑子和手里的一种设计直觉。最后再送一个小技巧当你使用责任链模式时请务必保证每个节点都能被清楚地命名日志信息里也要体现出当前节点的名称和请求流转到了哪一步。这条经验在线上问题排查时能帮你省下大量宝贵的时间。
返回列表