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 }

我們啟動,測試結果為:

  調用成功,任務按照我們的預期依次順序執行。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM