Java中使用责任链模式

一、问题引入

    在生活中,我们会遇到填写调查问卷的情况,比如中国移动推送的通话质量问卷、京东的购物体验问卷等等,这些问卷在生成之前往往会有一套复杂的逻辑,比如题目的跳转设置、不同题目之间的互斥设置、多选题的选项之间互斥设置,以及对答案的通过性判断等等。在这些背后,某些业务的实现就可以使用到本文所介绍的责任链模式,本文也将以保存用户答题作为模拟实例引入责任链模式。

二、责任链设计模式理论知识

2.1,责任链概念

    顾名思义,责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为模式。

    它的意图的是:避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。主要解决的问题是:职责链上的处理者负责处理请求,客户只需要将请求发送到职责链上即可,无须关心请求的处理细节和请求的传递,所以职责链将请求的发送者和请求的处理者解耦了。

从概念中我们可以知道,责任链模式的核心思想是,按照设计好的有序链条逐个自动执行每一个任务。这种设计模式在分类上属于行为设计模式。

2.2,责任链类图

 2.3,链的实现方式

责任链模式中的链,可以使用单向链表、List集合实现。个人感觉,单项链表在每个节点中包含下个节点的引用,在使用起来会比较方便,而且稳定。

三、责任链设计模式的应用

保存答题的具体场景为:先保存答题者,然后每个答题者可以回答多个问卷,所以答题者保存完成之后需要保存回答的是哪个答卷,最后保用户的答案。

我们用respondent单词表示答题者,用questionnaire表示答卷,用answer表示答案,在下面的代码实例中可根据单词的直译表示类的作用

下面将用实际的代码例子演示如何实现责任链,且默认使用的是SpringBoot框架。

首先我们创建责任链的处理类:RespondChainHandler

 1 /**
 2  * @Author Administrator
 3  * @Date 2021-02-17 15:30:01
 4  * @Version 1.0
 5  * @Description 责任链模式的具体执行handler
 6  */
 7 public abstract class RespondChainHandler {
 8     /**
 9      * 节点排序字段
10      * */
11     private int order;
12 
13     /**
14      * 下一个节点
15      * */
16     private RespondChainHandler next;
17 
18     /**
19      * 执行具体任务
20      *
21      * @param chainEntity 任务数据
22      */
23     protected abstract void doHandler(ChainEntity chainEntity);
24 
25 
26     public int getOrder() {
27         return order;
28     }
29 
30     public void setOrder(int order) {
31         this.order = order;
32     }
33 
34     public RespondChainHandler getNext() {
35         return next;
36     }
37 
38     public void setNext(RespondChainHandler next) {
39         this.next = next;
40     }
41 }

 然后创建责任链的核心类,即责任链调用类:RespondChain

 1 /**
 2  * @Author Administrator
 3  * @Date 2021-02-17 15:29:39
 4  * @Version 1.0
 5  * @Description 责任链模式执行保存答题任务
 6  */
 7 public class RespondChain {
 8     /**
 9      * 头节点
10      * */
11     private RespondChainHandler header;
12 
13     /**
14      * 任务执行入口
15      *
16      * @param chainEntity 数据
17      */
18     public void proceed(ChainEntity chainEntity) {
19         RespondChainHandler respond = header;
20         while (respond != null) {
21             respond.doHandler(chainEntity);
22             respond = respond.getNext();
23         }
24     }
25 
26     /**
27      * 添加具体任务handler到单向链表
28      *
29      * @param respond 任务handler
30      * @param order   排序,越小越靠前
31      */
32     public void addFilter(RespondChainHandler respond, int order) {
33         respond.setOrder(order);
34 
35         if (header == null) {
36             header = respond;
37             respond.setNext(null);
38         } else if (respond.getOrder() <= header.getOrder()) {//如果当前插入的排序小于header的排序,则插入到链表的头
39             //插入到链表的队首位置
40             respond.setNext(header);
41             header = respond;
42         } else {//插入到中间某一个位置
43             RespondChainHandler previous = header;
44             RespondChainHandler current = previous.getNext();
45             //寻找链表中符合当前order排序的位置
46             while (current != null) {
47                 if (respond.getOrder() <= current.getOrder()) {
48                     previous.setNext(respond);
49                     respond.setNext(current);
50                     break;
51                 } else {
52                     previous = current;
53                     current = previous.getNext();
54                 }
55             }
56             //队尾
57             if (current == null) {
58                 respond.setNext(null);
59                 previous.setNext(respond);
60             }
61         }
62     }
63 }

创建责任链处理的数据类:ChainEntity(这里名字起的不好,或许用DTO表示会更清晰)

 1 /**
 2  * @Author Administrator
 3  * @Date 2021-02-17 15:32:36
 4  * @Version 1.0
 5  * @Description 责任链需要处理的数据
 6  */
 7 @Data
 8 public class ChainEntity {
 9     //示例字段
10     private Integer id;
11     //示例字段
12     private String str1;
13     //示例字段
14     private String str2;
15     //示例字段
16     private List<Question> questions;
17 
18     @Data
19     public static class Question {
20         //示例字段
21         private Long questionId;
22         //示例字段
23         private String questionName;
24         //示例字段
25         private List<Answer> answers;
26 
27         @Data
28         public static class Answer{
29             //示例字段
30             private Long itemId;
31             //示例字段
32             private String itemContent;
33         }
34     }
35 }

处理类:RespondChainHandler是一个抽象类,具体的任务处理处理类要继承该类。RespondChainHandler处理类中有两个关键的地方:order和next,order用于加入单向链表时排序使用,next指向的是下一个节点。

调用类:RespondChain,header是单向链表的头节点,processd是任务执行入口,其中参数ChainEntity是外部传入的数据,作为责任链要处理的数据的载体。processd方法从header开始,先执行header节点里的doHandler任务,然后指向next节点,用while循环执行下去,直到没有更多的next节点。

下面我们创建具体的任务子类:

创建保存答题者任务子类:SaveRespondentClient

 1 /**
 2  * @Author Administrator
 3  * @Date 2021-02-17 15:30:22
 4  * @Version 1.0
 5  * @Description 保存答题者任务
 6  */
 7 @Component
 8 public class SaveRespondentClient extends RespondChainHandler {
 9 
10     @Override
11     protected void doHandler(ChainEntity chainEntity) {
12         System.out.println("保存答题者任务完成...");
13     }
14 }

创建保存答卷任务子类:SaveQuestionnaireClient

 1 /**
 2  * @Author Administrator
 3  * @Date 2021-02-17 15:30:45
 4  * @Version 1.0
 5  * @Description 保存答卷任务
 6  */
 7 @Component
 8 public class SaveQuestionnaireClient extends RespondChainHandler {
 9 
10     @Override
11     protected void doHandler(ChainEntity chainEntity) {
12         System.out.println("保存答卷任务完成...");
13     }
14 }

创建保存答案任务子类:SaveAnswerClient

 1 /**
 2  * @Author Administrator
 3  * @Date 2021-02-17 15:31:00
 4  * @Version 1.0
 5  * @Description 保存答案任务
 6  */
 7 @Component
 8 public class SaveAnswerClient extends RespondChainHandler {
 9 
10     @Override
11     protected void doHandler(ChainEntity chainEntity) {
12         System.out.println("保存答案任务完成...");
13     }
14 }

这三个子类处理自己职责范围内的事情。

然后我们创建外部调用类,处理保存答题业务,外部调用类用Controller模拟。我们默认使用的是SpringBoot框架,所以可以不用new对象,使用IOC容器即可。如果不使用SpringBoot当然是可以的,不过要记得将类实例化。

 1 /**
 2  * @Author Administrator
 3  * @Date 2021-02-17 15:37:08
 4  * @Version 1.0
 5  * @Description 责任链模式测试controller
 6  */
 7 @RestController
 8 @RequestMapping("/chain")
 9 public class ChainController {
10     //从IOC容器中取出处理类映射成Map,Map的key是处理类的类名,value是已实例化的子类
11     @Resource
12     private Map<String, RespondChainHandler> respondChainHandlerMap;
13 
14     @GetMapping(value = "save")
15     public String save(){
16         ChainEntity chainEntity =new ChainEntity();
17         RespondChain respondChain = new RespondChain();
18         respondChain.addFilter(respondChainHandlerMap.get("saveRespondentClient"), 1);
19         respondChain.addFilter(respondChainHandlerMap.get("saveQuestionnaireClient"), 2);
20         respondChain.addFilter(respondChainHandlerMap.get("saveAnswerClient"), 3);
21         //开始执行
22         respondChain.proceed(chainEntity);
23 
24         return "执行完成";
25     }
26 }

我们启动,测试结果为:

  调用成功,任务按照我们的预期依次顺序执行。

原文地址:https://www.cnblogs.com/huyueping/p/14407506.html