关于责任链模式:开源框架中的责任链模式实践

作者:vivo 互联网服务器团队-Wang Zhi责任链模式作为罕用的设计模式而被大家熟知和应用。本文介绍责任链的常见实现形式,并联合开源框架如Dubbo、Sentinel等进行延长探讨。 一、责任链介绍在GoF 的《设计模式》一书中对责任链模定义的:将申请的发送和接管解耦,让多个接管对象都有机会解决这个申请。将这些接管对象串成一条链,并沿着这条链传递这个申请,直到链上的某个接管对象可能解决它为止或者所有接管对象解决一遍。 用艰深的话解释在责任链模式中,多个处理器(接管对象)顺次解决同一个申请。一个申请先通过 A 处理器解决,而后再把申请传递给 B 处理器,B 处理器解决完后再传递给 C 处理器,以此类推,造成一个链条。链条上的每个处理器各自承当各自的解决职责,所以叫作责任链模式。 责任链模式无效地升高了发送和接收者之间的耦合度,加强了零碎的可扩展性。在责任链的模式下不仅可能针对单个处理器对象进行定制降级(每个处理器对象关注各自的工作),而且可能对整个责任链的处理器对象的程序的调整以及增删。 本文约定:责任链上的接管对象对立称为处理器;本文中介绍的责任链属于GOF定义中责任链的变种即责任链上的所有处理器都会参加工作的解决。 二、责任链实现责任链模式有多种实现形式,从驱动责任链上处理器形式的角度能够分类两类,即责任链驱动 和 责任链处理器自驱动。 2.1 处理器自驱动// 1、定义抽象类public abstract class AbstractHandler { protected Handler next = null; // 绑定处理器 public void setSuccessor(Handler next) { this.next = next; } // 处理器执行操作并驱动下一个处理器 public abstract void handle();} // 2、定义处理器Apublic class HandlerA extends AbstractHandler { @Override public void handle() { // do something if (next != null) { next.handle(); } }} // 3、定义处理器Bpublic class HandlerB extends AbstractHandler { @Override public void handle() { // do something if (next != null) { next.handle(); } }} // 4、构建责任链并增加处理器public class HandlerChain { // 通过链表的模式保留责任链 private AbstractHandler head = null; private AbstractHandler tail = null; public void addHandler(AbstractHandler handler) { handler.setSuccessor(null); if (head == null) { head = handler; tail = handler; return; } tail.setSuccessor(handler); tail = handler; } public void handle() { if (head != null) { head.handle(); } }} // 5、整体构建责任链增加处理器并进行驱动public class Application { public static void main(String[] args) { // 构建责任链并增加处理器 HandlerChain chain = new HandlerChain(); chain.addHandler(new HandlerA()); chain.addHandler(new HandlerB()); // 责任链负责触发 chain.handle(); }}阐明: ...

September 21, 2023 · 9 min · jiezi

关于责任链模式:一文带你读懂设计模式之责任链模式-京东云技术团队

1.前言emm,翻了一下之前刚入职时候的学习笔记,发现之前在熟悉业务代码的时候已经专门学习并整顿过过设计模式中的责任链模式,之前只是对其简略理解过罕用的设计模式有哪些,并未联合实例和源码深刻对其探索,利用相熟代码契机进行零碎学习并整顿文档如下。 2.什么是责任链模式?俗话说没有规矩不成方圆,咱们无论在工作还是生存中很多事件都须要依照规定的流程办事,这样的流程往往都是环环相扣的,上一部实现之后才会流转到下一步执行。比方咱们在做饭时都是先买菜、洗菜、切菜、炒菜、装盘在这样的过程中只有上一步实现之后能力开始下一步最初失去一道做好的菜;又比方在降职提名时,首先咱们要做一个述职报告进行述职,而后就是评审小组打分,评审小组筛选通过后,流转到项目组领导处审批,项目组领导依据述职报告和评审小组分数决定是否降职,项目组领导批准之后最初流转到部门领导审批并给出最初后果。像这种一步一步实现流程都能够通过责任链模式来实现。 简介: 责任链模式顾名思义是将不同职责的步骤串联起来执行,并且一个步骤执行实现之后才可能执行下一个步骤。从名字能够看出通常责任链模式应用链表来实现。因而当执行工作的申请发动时,从责任链上第一步开始往下传递,直到最初一个步骤实现。在责任链模式当中,客户端只用执行一次流程开始的申请便不再须要参加到流程执行当中,责任链上的流程便可能本人始终往下执行,客户端同样也并不关怀执行流程细节,从而实现与流程之间的解耦。 模式构造:责任链模式次要角色如下: ◦形象处理器(Handler):处理器形象接口,定义了解决申请的办法和执行下一步解决的处理器。 ◦具体处理器(ConcreteHandler):执行申请的具体实现,先依据申请执行解决逻辑,实现之后将申请交给下一个处理器执行。 ◦调用者:调用者通过创立处理器并将申请交给处理器进行解决。 相干代码:// 形象处理器public abstract class Handler { private Handler next; public Handler getNext() { return next; } public void setNext(Handler next) { this.next = next; } public abstract void handle(Object request);}// 具体处理器 1public class ConcreteHandler1 extends Handler { @Override public void handle(Object request) { System.out.println("concrete handler 1 execute request. request: " + request); if (getNext() != null) { getNext().handle(request); } }}// 具体处理器 2public class ConcreteHandler2 extends Handler { @Override public void handle(Object request) { System.out.println("concrete handler 2 execute request. request: " + request); if (getNext() != null){ getNext().handle(request); } }}// 具体处理器 3public class ConcreteHandler3 extends Handler { @Override public void handle(Object request) { System.out.println("concrete handler 3 execute request. request: " + request); if (getNext() != null) { getNext().handle(request); } }}public static void main(String[] args) { Handler concreteHandler1 = new ConcreteHandler1(); Handler concreteHandler2 = new ConcreteHandler2(); Handler concreteHandler3 = new ConcreteHandler3(); concreteHandler1.setNext(concreteHandler2); concreteHandler2.setNext(concreteHandler3); concreteHandler1.handle("my request.");}从下面的代码咱们能够看到其实责任链模式是非常简单的,然而其中有几个点须要留神一下: ...

August 18, 2023 · 5 min · jiezi

关于责任链模式:设计模式之责任链模式

本文通过图书馆管理系统中,用户名校验、明码校验、须要减少问题,每次都要减少if判断语句,将其改用责任链模式进行链式调用,为了让代码更加的优雅,咱们应用之前学过的建造者模式就代码进行革新。接着咱们会介绍责任链模式在咱们罕用的框架中的使用,最初是责任链模式的优缺点和利用场景。 读者能够拉取残缺代码到本地进行学习,实现代码均测试通过后上传到码云。 一、引出问题小王给老王打造了一套图书馆管理系统,随着访问量的一直减少,老王要求减少拜访的用户名校验。 小王说这有何难,说着就在用户拜访图书馆之前加了一层判断语句,判断用户名是否非法。过了一段时间后,又给每个用户颁发了一个明码,就须要在用户名校验通过当前校验明码。 小王就筹备在用户名的判断语句后,减少明码的校验语句。老王赶紧拦住了要持续更改代码的小王。如果当前再减少角色校验、权限校验、你筹备写多少个判断语句。 而且你把软件设计准则中的——开闭准则丢到哪里去了。 你能够思考应用一种模式,将所有的校验办法都独立进去一个类,每一个类只负责解决各自的校验逻辑,以后的校验类通过当前传递给下一个校验类进行解决,这样每次减少新的逻辑判断都只须要减少校验类就行了。 就像是一条流水线,每个类负责解决线上的一个环节。 二、责任链模式的概念和应用实际上,老王提出来的正是行为型设计模式中的——*责任链模式*。 责任链模式正如它的名字一样,将每个职责的步骤串联起来执行,并且一个步骤执行实现之后才可能执行下一个步骤。 从名字能够看出通常责任链模式应用链表来实现。 因而当执行工作的申请发动时,从责任链上第一步开始往下传递,直到最初一个步骤实现。 在责任链模式当中, 客户端只用执行一次流程开始的申请便不再须要参加到流程执行当中,责任链上的流程便可能本人始终往下执行, 客户端同样也并不关怀执行流程细节,从而实现与流程之间的解耦。 责任链模式须要有两个角色: 形象处理器(Handler):处理器形象接口,定义了解决申请的办法和执行下一步解决的处理器。 具体处理器(ConcreteHandler):执行申请的具体实现,先依据申请执行解决逻辑,实现之后将申请交给下一个处理器执行。 基于责任链模式实现图书馆的用户名校验和明码校验。 形象处理器: /** * @author tcy * @Date 22-08-2022 */public abstract class Handler { private Handler next; public Handler getNext() { return next; } public void setNext(Handler next) { this.next = next; } public abstract void handle(Object request);}用户名校验处理器: /** * @author tcy * @Date 23-08-2022 */public class ConcreteHandlerUsername extends Handler{ @Override public void handle(Object request) { //相应的业务逻辑... System.out.println("用户名校验通过. 参数: " + request); //调用链路中下一个节点的解决办法 if (getNext() != null) { getNext().handle(request); } }}明码校验器: ...

August 24, 2022 · 3 min · jiezi

不得不知的责任链设计模式

世界上最遥远的距离,不是生与死,而是它从你的世界路过无数次,你却选择视而不见,你无情,你冷酷啊...... 被你忽略的就是责任链设计模式,希望它再次经过你身旁你会猛的发现,并对它微微一笑...... 责任链设计模式介绍抽象介绍初次见面,了解表象,深入交流之后(看完文中的 demo 和框架中的实际应用后),你我便是灵魂之交(重新站在上帝视角来理解这个概念会更加深刻) 责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式。在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象能或不能处理该请求,它都会把相同的请求传给下一个接收者,依此类推,直至责任链结束。接下来将概念图形化,用大脑图形处理区理解此概念 上图左侧的 UML 类图中,Sender 类不直接引用特定的接收器类。 相反,Sender 引用Handler 接口来处理请求handler.handleRequest(),这使得 Sender 独立于具体的接收器(概念当中说的解耦) Receiver1,Receiver2 和 Receiver3 类通过处理或转发请求来实现 Handler 接口(取决于运行时条件)上图右侧的 UML 序列图显示了运行时交互,在此示例中,Sender 对象在 receiver1 对象(类型为Handler)上调用 handleRequest(), 接收器 1 将请求转发给接收器 2,接收器 2 又将请求转发到处理(执行)请求的接收器3具象介绍大家小时候都玩过击鼓传花的游戏,游戏的每个参与者就是责任链中的一个处理对象,花球就是待处理的请求,花球就在责任链(每个参与者中)进行传递,只不过责任链的结束时间点是鼓声的结束. 来看 Demo 和实际案例 Demo设计程序猿和 log 是老交情了,使用 logback 配置日志的时候有 ConsoleAppender 和 RollingFileAppender,这两个 Appender 就组成了一个 log 记录的责任链。下面的 demo 就是模拟 log 记录:ConsoleLogger 打印所有级别的日志;EmailLogger 记录特定业务级别日志 ;FileLogger 中只记录 warning 和 Error 级别的日志 抽象概念介绍中,说过实现责任链要有一个抽象接收器接口,和具体接收器,demo 中 Logger 就是这个抽象接口,由于该接口是 @FunctionalInterface (函数式接口), 它的具体实现就是 Lambda 表达式,关键代码都已做注释标注 ...

June 21, 2019 · 4 min · jiezi

行为型模式:责任链模式

LieBrother原文:行为型模式:责任链模式十一大行为型模式之四:责任链模式。简介姓名 :责任链模式英文名 :Chain of Responsibility Pattern价值观 :责任归我个人介绍 :Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request.Chain the receiving objects and pass the request along the chain until an object handles it.使多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。(来自《设计模式之禅》)你要的故事快要金三银四了,很多同学蠢蠢欲动想要去外面看看世界,而大家有没有提前了解各大企业的面试流程呢?这里我就给大家科普一下大多数互联网企业的面试流程,正好责任链模式用得上。在互联网企业中,程序员这个岗位的招聘流程大同小异,而一般至少都会有 3 轮面试,分别是 2 轮技术面和 1 轮 HR 面。而这几轮面试都是层层递进的,最开始面试一般是组长面试,通过之后就是部门领导面试,再通过之后就是 HR 面试,HR 面试通过之后就可以成功拿到 Offer 了。故事从小明参加某公司的面试开始,某公司的招聘流程就是上面说的 3 轮面试。招聘流程的面试官分别是:第一面是组长老刚,第二面是部门经理老孙,第三面也就是终面是 HR 老刘。为什么说这个场景符合责任链模式呢?首先不管是组长还是部门经理还是 HR,他们都作为面试官,面试官赋予他们的权利是去面试来公司面试的同学,而面试的结果是可传递性的,也就是如果面试通过,就会到下一轮面试,最终成为一条传递链。我们用代码模拟这个过程。public class ChainOfResponsibilityTest { public static void main(String[] args) { Interviewee interviewee = new Interviewee(“小明”); TeamLeader teamLeader = new TeamLeader(“老刚”); DepartMentManager departMentManager = new DepartMentManager(“老孙”); HR hr = new HR(“老刘”); // 设置面试流程 teamLeader.setNextInterviewer(departMentManager); departMentManager.setNextInterviewer(hr); // 开始面试 teamLeader.handleInterview(interviewee); }}/** * 面试者 /class Interviewee { private String name; private boolean teamLeaderOpinion; private boolean departMentManagerOpinion; private boolean hrOpinion; public Interviewee(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } public boolean isTeamLeaderOpinion() { return teamLeaderOpinion; } public void setTeamLeaderOpinion(boolean teamLeaderOpinion) { this.teamLeaderOpinion = teamLeaderOpinion; } public boolean isDepartMentManagerOpinion() { return departMentManagerOpinion; } public void setDepartMentManagerOpinion(boolean departMentManagerOpinion) { this.departMentManagerOpinion = departMentManagerOpinion; } public boolean isHrOpinion() { return hrOpinion; } public void setHrOpinion(boolean hrOpinion) { this.hrOpinion = hrOpinion; }}/* * 面试官 /abstract class Interviewer { protected String name; protected Interviewer nextInterviewer; public Interviewer(String name) { this.name = name; } public Interviewer setNextInterviewer(Interviewer nextInterviewer) { this.nextInterviewer = nextInterviewer; return this.nextInterviewer; } public abstract void handleInterview(Interviewee interviewee);}/* * 组长 /class TeamLeader extends Interviewer { public TeamLeader(String name) { super(name); } @Override public void handleInterview(Interviewee interviewee) { System.out.println(“组长[” + this.name + “]面试[” + interviewee.getName() + “]同学”); interviewee.setTeamLeaderOpinion(new Random().nextBoolean()); if (interviewee.isTeamLeaderOpinion()) { System.out.println("[" + interviewee.getName() + “]同学组长轮面试通过”); this.nextInterviewer.handleInterview(interviewee); } else { System.out.println("[" + interviewee.getName() + “]同学组长轮面试不通过”); } }}/* * 部门经理 /class DepartMentManager extends Interviewer { public DepartMentManager(String name) { super(name); } @Override public void handleInterview(Interviewee interviewee) { System.out.println(“部门经理[” + this.name + “]面试[” + interviewee.getName() + “]同学”); interviewee.setDepartMentManagerOpinion(new Random().nextBoolean()); if (interviewee.isDepartMentManagerOpinion()) { System.out.println("[" + interviewee.getName() + “]同学部门经理轮面试通过”); this.nextInterviewer.handleInterview(interviewee); } else { System.out.println("[" + interviewee.getName() + “]同学部门经理轮面试不通过”); } }}/* * HR */class HR extends Interviewer { public HR(String name) { super(name); } @Override public void handleInterview(Interviewee interviewee) { System.out.println(“HR[” + this.name + “]面试[” + interviewee.getName() + “]同学”); interviewee.setHrOpinion(new Random().nextBoolean()); if (interviewee.isHrOpinion()) { System.out.println("[" + interviewee.getName() + “]同学HR轮面试通过, 恭喜拿到 Offer”); } else { System.out.println("[" + interviewee.getName() + “]同学HR轮面试不通过”); } }}打印结果:组长[老刚]面试[小明]同学[小明]同学组长轮面试通过部门经理[老孙]面试[小明]同学[小明]同学部门经理轮面试通过HR[老刘]面试[小明]同学[小明]同学HR轮面试通过, 恭喜拿到 Offer上面代码打印结果是小明通过层层筛选,通过了面试,拿到了 Offer。下面的图来展现这次面试的流程。讲解一下代码,Interviewee 是面试者,对于企业来说这个面试者来面试的过程会有 3 个关键标识,就是 3 位面试官对这位面试者的评价,只有都评价好才能通过面试拿到 Offer,所以 Interviewee 类有 3 位面试官的面试结果。Interviewer 是面时官,企业中面试官不是一个职位,而是一个工作,帮企业招到合适的人才,所以它是一个抽象类,有一个抽象方法就是去面试应聘者,具体面试过程又各面试官实现,而因为这个面试会有结果反馈,结果好的会进入下一轮面试,所以会有下一个面试官的对象引用,责任链模式也就在这里体现。TeamLeader、DepartMentManager、HR 则为公司的不同职位,而这些职位当公司需要招聘时,都需要去充当面试官,所以都继承了 Interviewer。这整个过程就构成了责任链模式代码示例,希望在金三银四各位蠢蠢欲动的朋友们都能闯到最后一关拿下 HR 姐姐。代码:Chain of Responsibility Pattern总结责任链模式很好的把处理的逻辑封装起来,在代码中,我们看到的只是组长面试,但是其实背后隐藏着部门经理和HR的面试。责任链是不是看起来很熟悉?在开发 Java Web 项目的时候是不是有用到过?Filter 过滤器里面就是用责任链模式来实现的。上面代码还用到了另一个模式,不明确指出来了,考验大家学习这些设计模式的效果,知道的同学可以留言说一下。参考资料:《大话设计模式》、《设计模式之禅》推荐阅读:公众号之设计模式系列文章希望文章对您有所帮助,设计模式系列会持续更新,感兴趣的同学可以关注公众号:LieBrother,第一时间获取文章推送阅读,也可以一起交流,交个朋友。 ...

February 25, 2019 · 2 min · jiezi

利用责任链模式设计一个拦截器

前言近期在做 Cicada 的拦截器功能,正好用到了责任链模式。这个设计模式在日常使用中频率还是挺高的,借此机会来分析分析。责任链模式先来看看什么是责任链模式。引用一段维基百科对其的解释:责任链模式在面向对象程式设计里是一种软件设计模式,它包含了一些命令对象和一系列的处理对象。每一个处理对象决定它能处理哪些命令对象,它也知道如何将它不能处理的命令对象传递给该链中的下一个处理对象。该模式还描述了往该处理链的末尾添加新的处理对象的方法。光看这段描述可能大家会觉得懵,简单来说就是该设计模式用于对某个对象或者请求进行一系列的处理,这些处理逻辑正好组成一个链条。下面来简单演示使用与不使用责任链模式有什么区别和优势。责任链模式的应用传统实现假设这样的场景:传入了一段内容,需要对这段文本进行加工;比如过滤敏感词、错别字修改、最后署上版权等操作。常见的写法如下:public class Main { public static void main(String[] args) { String msg = “内容内容内容” ; String result = Process.sensitiveWord() .typo() .copyright(); }}这样看似没啥问题也能解决需求,但如果我还需要为为内容加上一个统一的标题呢?在现有的方式下就不得不新增处理方法,并且是在这个客户端(Process)的基础上进行新增。显然这样的扩展性不好。责任链模式实现这时候就到了责任链模式发挥作用了。该需求非常的符合对某一个对象、请求进行一系列处理的特征。于是我们将代码修改:这时 Process 就是一个接口了,用于定义真正的处理函数。public interface Process { /** * 执行处理 * @param msg / void doProcess(String msg) ;}同时之前对内容的各种处理只需要实现该接口即可:public class SensitiveWordProcess implements Process { @Override public void doProcess(String msg) { System.out.println(msg + “敏感词处理”); }}public class CopyrightProcess implements Process { @Override public void doProcess(String msg) { System.out.println(msg + “版权处理”); }}public class CopyrightProcess implements Process { @Override public void doProcess(String msg) { System.out.println(msg + “版权处理”); }}然后只需要给客户端提供一个执行入口以及添加责任链的入口即可:public class MsgProcessChain { private List<Process> chains = new ArrayList<>() ; /* * 添加责任链 * @param process * @return / public MsgProcessChain addChain(Process process){ chains.add(process) ; return this ; } /* * 执行处理 * @param msg */ public void process(String msg){ for (Process chain : chains) { chain.doProcess(msg); } }}这样使用起来就非常简单:public class Main { public static void main(String[] args) { String msg = “内容内容内容==” ; MsgProcessChain chain = new MsgProcessChain() .addChain(new SensitiveWordProcess()) .addChain(new TypoProcess()) .addChain(new CopyrightProcess()) ; chain.process(msg) ; }}当我需要再增加一个处理逻辑时只需要添加一个处理单元即可(addChain(Process process)),并对客户端 chain.process(msg) 是无感知的,不需要做任何的改动。可能大家没有直接写过责任链模式的相关代码,但不经意间使用到的却不少。比如 Netty 中的 pipeline 就是一个典型的责任链模式,它可以让一个请求在整个管道中进行流转。通过官方图就可以非常清楚的看出是一个责任链模式:用责任链模式设计一个拦截器对于拦截器来说使用责任链模式再好不过了。下面来看看在 Cicada 中的实现:首先是定义了和上文 Process 接口类似的 CicadaInterceptor 抽象类:public abstract class CicadaInterceptor { public boolean before(CicadaContext context,Param param) throws Exception{ return true; } public void after(CicadaContext context,Param param) throws Exception{}}同时定义了一个 InterceptProcess 的客户端:其中的 loadInterceptors() 会将所有的拦截器加入到责任链中。再提供了两个函数分别对应了拦截前和拦截后的入口:实际应用现在来看看具体是怎么使用的吧。在请求的 handle 中首先进行加载(loadInterceptors(AppConfig appConfig)),也就是初始化责任链。接下来则是客户端的入口;调用拦截前后的入口方法即可。由于是拦截器,那么在 before 函数中是可以对请求进行拦截的。只要返回 false 就不会继续向后处理。所以这里做了一个返回值的判断。同时对于使用者来说只需要创建拦截器类继承 CicadaInterceptor 类即可。这里做了一个演示,分别有两个拦截器:记录一个业务 handle 的执行时间。在 after 里打印了请求参数。同时可在第一个拦截器中返回 false 让请求被拦截。先来做前两个试验:这样当我请求其中一个接口时会将刚才的日志打印出来:接下来我让打印执行时间的拦截器中拦截请求,同时输入向前端输入一段文本:请求接口可以看到如下内容:同时后面的请求参数也没有打印出来,说明请求确实被拦截下来。同时我也可以调整拦截顺序,只需要在 @Interceptor(order = 1) 注解中定义这个 order 属性即可(默认值是 0,越小越先执行)。之前是打印请求参数的拦截器先执行,这次我手动将它的 order 调整为 2,而打印时间的 order 为 1 。再次请求接口观察后台日志:发现打印执行时间的拦截器先执行。那这个执行执行顺序如何实现自定义配置的呢?其实也比较简单,有以下几步:在加载拦截器时将注解里的 order 保存起来。设置拦截器到责任链中时通过反射将 order 的值保存到各个拦截器中。最终通过排序重新排列这个责任链的顺序。贴一些核心代码。扫描拦截器时保存 order 值:保存 order 值到拦截器中:重新对责任链排序:总结整个责任链模式已经讲完,希望对这个设计模式还不了解的朋友带来些帮助。上文中的源码如下:https://github.com/TogetherOS/cicada:一个高性能、轻量 HTTP 框架https://git.io/fxKid欢迎关注公众号一起交流: ...

October 22, 2018 · 2 min · jiezi