一、分支调用责任链模式

定义:
  创建多个对象,使这些对象形成一条链,并沿着这条链传递请求,直到链上的某一个对象决定处理此请求。

特点:
1)接收请求的对象连接成一条链,对象之间存在层级关系。
2)这些对象可处理请求,也可传递请求,直到有对象处理该请求。

具体情节:

今天来说说程序员小猿和产品就关于需求发生的故事。前不久,小猿收到了产品的需求。

产品经理:小猿,为了迎合大众屌丝用户的口味,我们要放一张图,要露点的。

小猿:……露点?你大爷的,让身为正义与纯洁化身的我做这种需求,还露点。

产品经理:误会误会,是放一张暴露一点点的,尺寸不大。

小猿:尼玛~能说清楚些吗,需求模棱两可的。不干,我要上报boss。

产品经理也一阵无语,这豆丁的事还上报boss。话说这上报也得走程序是吧,技术经理就不干了,“凭什么要跳过我,得经过我才能到boss。这一层一层上报就涉及到这次的责任链模式

image.png
责任链模式涉及到的角色如下所示:
  - 抽象处理者角色:定义了处理请求的接口或者抽象类,提供了处理请求的的方法和设置下一个处理者的方法。
  - 具体处理者角色:实现或者继承抽象这角色,具体逻辑根据实际的架构来定,后面会提到。

1.2、实战

先来看抽象处理者角色代码:

  1. public abstract class Handler {
  2. private Handler nextHandler;
  3. private int level;
  4. public Handler(int level) {
  5. this.level = level;
  6. }
  7. // 处理请求传递,注意final,子类不可重写
  8. public final void handleMessage(Demand demand) {
  9. if (level == demand.demandLevel()) {
  10. this.report(demand);
  11. } else {
  12. if (this.nextHandler != null) {
  13. System.out.println("事情太严重,需报告上一级");
  14. this.nextHandler.handleMessage(demand);
  15. } else {
  16. System.out.println("我就是boss,没有上头");
  17. }
  18. }
  19. }
  20. public void setNextHandler(Handler handler) {
  21. this.nextHandler = handler;
  22. }
  23. // 抽象方法,子类实现
  24. public abstract void report(Demand demand);
  25. }

在抽象处理者角色定义了处理请求的抽象方法,以及下一级传递的对象方法,重点在handleMessage处理请求传递的方法,下面会解释为什么要这样写,继续往下看.

下面是具体处理者角色,继承抽象处理者角色,在我们情景中有两个具体处理者,分别是技术经理和boss。

  1. // 技术经理
  2. public class TechnicalManager extends Handler {
  3. public TechnicalManager() {
  4. super(1);
  5. }
  6. @Override
  7. public void report(Demand demand) {
  8. System.out.println("需求:" + demand.detail());
  9. System.out.println(getClass().getSimpleName() + ":小猿我挺你,这个需求不干");
  10. }
  11. }
  12. // boss
  13. public class Boss extends Handler {
  14. public Boss() {
  15. super(2);
  16. }
  17. @Override
  18. public void report(Demand demand) {
  19. System.out.println("需求:" + demand.detail());
  20. System.out.println(getClass().getSimpleName() + ":你们打一架吧,打赢的做决定");
  21. }
  22. }

可以看到具体处理者的代码很简洁,重写了report方法,实现各自的业务逻辑,这都归功于父类中handleMessage这个方法。

两个角色都定义好了,来看客户端如何实现:

  1. public class Client {
  2. public static void main(String[] args) {
  3. Demand demandA = new DemandA(); // 请求等级低
  4. Demand demandB = new DemandB(); // 请求等级高
  5. Boss boss = new Boss();
  6. TechnicalManager technicalManager = new TechnicalManager();
  7. technicalManager.setNextHandler(boss); // 设置下一级
  8. technicalManager.handleMessage(demandA);
  9. System.out.println("============================");
  10. technicalManager.handleMessage(demandB);
  11. }
  12. }

可以看到在客户端中的重点是设置下一级的处理者,这样多个处理者对象就会形成一条链。运行客户端,结果如下:

需求:加一张露一点点的图

TechnicalManager:小猿我挺你,这个需求不干

需求:加一张露一点点的图 TechnicalManager:事情太严重,需报告上一级 Boss:你们打一架吧,打赢的做决定

从结果可以看到,级别低的请求技术经理自己处理,级别高的传递给了下一级的Boss,这样就形成一条链,而这也是责任链的核心所在。注意,在请求的传递过程中,请求是不会发生变化的。需求不会从“加一张露一点点的图”变成了“加一张露点的图”,这等着boss请到办公室喝茶吧。

1.3、扩展

责任链+模板方法
**
回头看看上面的代码,抽象类中定义了几个方法,一个是final修饰的handleMessage,一个是抽象方法report,还有一个是setNextHandler。这刚好是模板方法模式中的三个基本方法,分别是具体方法(抽象类声明并实现,子类不实现)、抽象方法(抽象类声明,子类必须实现)、钩子方法(抽象类声明并实现,子类可扩展)。handleMessage方法加了final修饰,子类不可重写,而handleMessage正是把传递请求工作交给父类Handler,子类不需要处理传递的工作。而report则是抽象方法,子类必须重写该方法,子类处理请求的业务逻辑。setNextHandler是钩子方法,在这里我们并没有实现。

这样结合模板方法模式的好处在哪?首先加了handleMessage方法,把请求的传递判断从子类中剥离出来,让子类在report方法中专心处理请求的业务逻辑,做到了单一职责原则。再者是子类的实现变得简单了,不需要进行传递的判断,非常有利于快速扩展。

1.4、责任链模式的优缺点

优点

1)降低耦合度:客户端不需要知道请求由哪个处理者处理,而处理者也不需要知道处理者之间的传递关系,由系统灵活的组织和分配。
2)良好的扩展性:增加处理者的实现很简单,只需重写处理请求业务逻辑的方法。

缺点

1)请求会从链头发出,直到有处理者响应,在责任链比较长的时候会影响系统性能。
2)请求递归,调试排错比较麻烦。

1.5、总结

责任链模式在实际项目中可以用到的地方还是比较多的,比如会员等级系统,会员等级之间构成一条链,用户发起一个请求,系统只要把请求分发到责任链模式的入口,直到传递到与用户会员匹配的等级,这样各个会员等级的业务逻辑就会变成很清晰。

二、链式调用的责任链模式

一个纯的责任链模式要求一个具体的处理者对象只能在两个行为中选择一个:一是承担责任,而是把责任推给下家。不允许出现某一个具体处理者对象在承担了一部分责任后又 把责任向下传的情况。在一个纯的责任链模式里面,一个请求必须被某一个处理者对象所接收;

假设这样的场景:

:::info 传入了一段内容,需要对这段文本进行加工;比如过滤敏感词、错别字修改、最后署上版权等操作。 :::

2.1 传统实现

  1. public class Main {
  2. public static void main(String[] args) {
  3. String msg = "内容内容内容" ;
  4. String result = Process.sensitiveWord()
  5. .typo()
  6. .copyright();
  7. }
  8. }

这样看似没啥问题也能解决需求,但如果我还需要为为内容加上一个统一的标题呢?在现有的方式下就不得不新增处理方法,并且是在这个客户端(Process)的基础上进行新增。这就违反了设计模式中的SRP(开闭原则)。显然这样的扩展性不好。

2.2 责任链模式实现

image.png
这时 Process 就是一个接口了,用于定义真正的处理函数。

  1. public interface Process {
  2. /**
  3. * 执行处理
  4. * @param msg
  5. */
  6. String doProcess(String msg) ;
  7. }

同时之前对内容的各种处理只需要实现该接口即可:

  1. public class SensitiveWordProcess implements Process {
  2. @Override
  3. public String doProcess(String msg) {
  4. System.out.println(msg + "敏感词处理");
  5. String noSensitiveWord = msg;
  6. return noSensitiveWord
  7. }
  8. }
  9. public class TypoProcess implements Process {
  10. @Override
  11. public String doProcess(String msg) {
  12. System.out.println(msg + "错别字处理");
  13. String noTypoWord = msg;
  14. return noTypoWord
  15. }
  16. }
  17. public class CopyrightProcess implements Process {
  18. @Override
  19. public String doProcess(String msg) {
  20. System.out.println(msg + "版权处理");
  21. String copyrightWord = msg;
  22. return copyrightWord
  23. }
  24. }

然后只需要给客户端提供一个执行入口以及添加责任链的入口即可:

  1. public class MsgProcessChain {
  2. private List<Process> chains = new ArrayList<>() ;
  3. /**
  4. * 添加责任链
  5. * @param process
  6. * @return
  7. */
  8. public MsgProcessChain addChain(Process process){
  9. chains.add(process) ;
  10. return this ;
  11. }
  12. /**
  13. * 执行处理
  14. * @param msg
  15. */
  16. public String process(String msg){
  17. String resultMsg = msg;
  18. for (Process chain : chains) {
  19. procrssMsg = chain.doProcess(resultMsg);
  20. resultMsg = procrssMsg;
  21. }
  22. return resultMsg;
  23. }
  24. }

这样使用起来就非常简单:

  1. public class Main {
  2. public static void main(String[] args) {
  3. String msg = "内容内容内容==" ;
  4. MsgProcessChain chain = new MsgProcessChain()
  5. .addChain(new SensitiveWordProcess())
  6. .addChain(new TypoProcess())
  7. .addChain(new CopyrightProcess()) ;
  8. result = chain.process(msg) ;
  9. }
  10. }

当我需要再增加一个处理逻辑时只需要添加一个处理单元即可(addChain(Process process)),并对客户端 chain.process(msg) 是无感知的,不需要做任何的改动。

三、实现拦截器

定义抽象的拦截器:

  1. public abstract class CicadaInterceptor {
  2. private int order ;
  3. public int getOrder() {
  4. return order;
  5. }
  6. public void setOrder(int order) {
  7. this.order = order;
  8. }
  9. /**
  10. * before
  11. * @param context
  12. * @param param
  13. * @return
  14. * true if the execution chain should proceed with the next interceptor or the handler itself
  15. * @throws Exception
  16. */
  17. protected boolean before(CicadaContext context,Param param) throws Exception{
  18. return true;
  19. }
  20. /**
  21. * after
  22. * @param context
  23. * @param param
  24. * @throws Exception
  25. */
  26. protected void after(CicadaContext context,Param param) throws Exception{}
  27. }

https://juejin.im/post/5bd95adfe51d45607e02ecc0