• 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 }

    我们启动,测试结果为:

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

  • 相关阅读:
    log4cpp
    互斥锁封装
    Educational Codeforces Round 37-F.SUM and REPLACE (线段树,线性筛,收敛函数)
    Codeforces 920E-Connected Components? (set,补图,连通块)
    Persistent Bookcase CodeForces
    P4390 [BOI2007]Mokia 摩基亚 (CDQ解决三维偏序问题)
    P3157 [CQOI2011]动态逆序对 (CDQ解决三维偏序问题)
    CDQ 分治解决和点对有关的问题
    洛谷 P2163 [SHOI2007]园丁的烦恼 (离线sort,树状数组,解决三维偏序问题)
    洛谷 P3469 [POI2008]BLO-Blockade (Tarjan,割点)
  • 原文地址:https://www.cnblogs.com/huyueping/p/14407506.html
Copyright © 2020-2023  润新知