目錄
1. 單例責任鏈
2. 多例責任鏈
核心區別對比
實際應用場景
單例實現
多例實現
初始化
初始化責任鏈
執行測試方法
歡迎關注我的博客!26屆java選手,一起加油💘💦👨?🎓😄😂
最近在學習項目的時候學到了責任鏈的設計模式,覺得很有趣,但對我來說也很有挑戰,寫一篇文章記錄我是如何弄懂這個設計模式和帶著例子的全鏈路解析。
責任鏈模式,責任鏈模式是一種行為設計模式,它允許你將請求沿著處理者鏈進行傳遞,直到有一個處理者能夠處理該請求。在這個具體的代碼中,主要用于構建一個規則處理鏈,不同的規則可以依次處理請求。
單例責任鏈和多例責任鏈的區別主要體現在實例管理方式和應用場景上,以下是具體對比:
1. 單例責任鏈
- 定義:整個責任鏈在系統中全局唯一,所有請求共享同一個鏈實例,鏈中的每個節點(處理者)也通常是單例。
- 特點:
- 線程安全風險:若責任鏈允許動態修改(如追加節點),需考慮線程安全問題(如使用?
ConcurrentHashMap
?或加鎖)。 - 固定結構:鏈結構一旦初始化完成,通常不會改變(除非主動修改)。
- 資源高效:內存中僅存在一個鏈實例,適合穩定且高頻使用的場景。
- 線程安全風險:若責任鏈允許動態修改(如追加節點),需考慮線程安全問題(如使用?
- 示例:
java
@Service public class Rule01TradeRuleFactory {@Resource private RuleLogic101 ruleLogic101;@Resource private RuleLogic102 ruleLogic102;public ILogicLink openLogicLink() {// 單例鏈:全局共享同一個 ruleLogic101 和 ruleLogic102 實例ruleLogic101.appendNext(ruleLogic102); return ruleLogic101;} }
2. 多例責任鏈
- 定義:每次使用責任鏈時動態創建新實例,鏈結構和節點可能每次不同。
- 特點:
- 線程安全:每個鏈實例獨立,無并發問題。
- 靈活性高:可根據需求動態組合節點(如 A→B→C 或 A→C)。
- 資源消耗:每次創建新鏈,適合低頻或需要靈活配置的場景。
- 示例:
java
@Service public class MultiInstanceRuleFactory {@Resource private RuleLogic101 ruleLogic101;@Resource private RuleLogic102 ruleLogic102;public ILogicLink createLink(boolean useNode2) {// 多例鏈:每次返回新的鏈結構RuleLogic101 newNode1 = new RuleLogic101(); if (useNode2) {newNode1.appendNext(new RuleLogic102()); }return newNode1;} }
核心區別對比
維度 | 單例責任鏈 | 多例責任鏈 |
---|---|---|
實例數量 | 全局唯一 | 每次使用時創建新實例 |
結構靈活性 | 固定(需手動修改) | 動態組合(如條件添加節點) |
線程安全 | 需額外處理(如加鎖) | 天然線程安全 |
適用場景 | 高頻、穩定的請求處理 | 低頻、動態配置的請求處理 |
資源消耗 | 低 | 較高(每次創建新對象) |
實際應用場景
- 單例責任鏈:電商風控規則鏈、支付流程校驗鏈(規則固定且高頻調用)。
- 多例責任鏈:動態任務編排、個性化業務流程(如用戶自定義審批流)。
單例實現
ILogicChainArmory<T, D, R>:該接口定義了責任鏈的基本操作,即獲取下一個處理者(next())和追加下一個處理者(appendNext())。這樣的設計使得責任鏈中的每個處理者都可以動態地連接其他處理者,形成一個鏈式結構。
ILogicLink<T, D, R>:繼承自?ILogicChainArmory<T, D, R>
,并額外定義了?apply
?方法,用于處理請求。apply
?方法接收請求參數?requestParameter
?和動態上下文?dynamicContext
,并返回處理結果。
AbstractLogicLink<T, D, R>
:實現了?ILogicLink<T, D, R>
?接口,提供了?next
?屬性和?next()
、appendNext()
?方法的基本實現。同時,還提供了一個受保護的?next
?方法,用于調用下一個處理者的?apply
?方法,從而實現請求的傳遞。
public interface ILogicChainArmory<T, D, R> {ILogicLink<T, D, R> next();ILogicLink<T, D, R> appendNext(ILogicLink<T, D, R> next);}public interface ILogicLink<T, D, R> extends ILogicChainArmory<T, D, R> {R apply(T requestParameter, D dynamicContext) throws Exception;}public abstract class AbstractLogicLink<T, D, R> implements ILogicLink<T, D, R> {private ILogicLink<T, D, R> next;@Overridepublic ILogicLink<T, D, R> next() {return next;}@Overridepublic ILogicLink<T, D, R> appendNext(ILogicLink<T, D, R> next) {this.next = next;return next;}protected R next(T requestParameter, D dynamicContext) throws Exception {return next.apply(requestParameter, dynamicContext);}}
具體實現:
定義兩個實現:
@Slf4j
@Service
public class RuleLogic101 extends AbstractLogicLink<String, Rule02TradeRuleFactory.DynamicContext, String>{@Overridepublic String apply(String requestParameter, Rule01TradeRuleFactory.DynamicContext dynamicContext) throws Exception {log.info("link model01 RuleLogic101");return next(requestParameter, dynamicContext);}}@Slf4j
@Service
public class RuleLogic102 extends AbstractLogicLink<String, Rule02TradeRuleFactory.DynamicContext, String>{@Overridepublic String apply(String requestParameter, Rule01TradeRuleFactory.DynamicContext dynamicContext) throws Exception {log.info("link model01 RuleLogic102");return "link model01 單實例鏈";}}
Rule01TradeRuleFactory
?類是一個工廠類,它的主要作用是創建和組裝責任鏈。在這個責任鏈中,RuleLogic101
?和?RuleLogic102
?是具體的規則處理器,通過工廠類將它們連接成一個鏈,以便按順序處理請求。
@Service
public class Rule01TradeRuleFactory {@Resourceprivate RuleLogic101 ruleLogic101;@Resourceprivate RuleLogic102 ruleLogic102;public ILogicLink<String, Rule02TradeRuleFactory.DynamicContext, String> openLogicLink() {ruleLogic101.appendNext(ruleLogic102);return ruleLogic101;}@Data@Builder@AllArgsConstructor@NoArgsConstructorpublic static class DynamicContext {private String age;}}
測試方法
@Testpublic void test_model01_01() throws Exception {ILogicLink<String, Rule02TradeRuleFactory.DynamicContext, String> logicLink = rule01TradeRuleFactory.openLogicLink();String logic = logicLink.apply("123", new Rule02TradeRuleFactory.DynamicContext());log.info("測試結果:{}", JSON.toJSONString(logic));}
執行openLogicLink方法
在執行appenNext的時候進行責任鏈的組裝:RuleLogic101->RuleLogic102,并返回
然后就能執行責任鏈頭節點的apply方法,這里return next()就是在AbstractLogicLink里的
protected R next(T requestParameter, D dynamicContext) throws Exception {return next.apply(requestParameter, dynamicContext);}
也就是去執行了RuleLogic102的apply方法:
到此單例的責任鏈就結束
多例實現
鏈表接口:
public interface ILink<E> {boolean add(E e);boolean addFirst(E e);boolean addLast(E e);boolean remove(Object o);E get(int index);void printLinkList();}
鏈表實現
/*** @description 雙向鏈表基礎實現類(責任鏈的底層數據結構)* @param <E> 鏈表存儲的元素類型(這里為 ILogicHandler 實現類)*/
public class LinkedList<E> implements ILink<E> {/** 鏈表名稱(用于標識不同責任鏈) */private final String name;/** 鏈表元素數量(transient 表示序列化時忽略該字段) */transient int size = 0;/** 頭節點引用 */transient Node<E> first;/** 尾節點引用 */transient Node<E> last;/*** 構造函數* @param name 鏈表名稱*/public LinkedList(String name) {this.name = name;}// ============================ 節點操作方法 ============================/*** 在鏈表頭部插入新節點* @param e 待插入的元素*/private void linkFirst(E e) {final Node<E> oldFirst = first; // 保存原頭節點final Node<E> newNode = new Node<>(null, e, oldFirst); // 創建新節點,前驅為 null,后繼為原頭節點first = newNode; // 更新頭節點為新節點// 若原頭節點為空(鏈表為空)if (oldFirst == null) {last = newNode; // 同時更新尾節點} else {oldFirst.prev = newNode; // 原頭節點的前驅指向新節點}size++; // 元素數量加 1}/*** 在鏈表尾部插入新節點* @param e 待插入的元素*/private void linkLast(E e) {final Node<E> oldLast = last; // 保存原尾節點final Node<E> newNode = new Node<>(oldLast, e, null); // 創建新節點,前驅為原尾節點,后繼為 nulllast = newNode; // 更新尾節點為新節點// 若原尾節點為空(鏈表為空)if (oldLast == null) {first = newNode; // 同時更新頭節點} else {oldLast.next = newNode; // 原尾節點的后繼指向新節點}size++; // 元素數量加 1}// ============================ ILink 接口實現 ============================/*** 默認將元素添加到鏈表尾部(接口方法)* @param e 待添加的元素* @return 添加成功返回 true*/@Overridepublic boolean add(E e) {linkLast(e); // 調用尾部插入方法return true;}/*** 在鏈表頭部添加元素(接口方法)* @param e 待添加的元素* @return 添加成功返回 true*/@Overridepublic boolean addFirst(E e) {linkFirst(e); // 調用頭部插入方法return true;}/*** 在鏈表尾部添加元素(接口方法)* @param e 待添加的元素* @return 添加成功返回 true*/@Overridepublic boolean addLast(E e) {linkLast(e); // 調用尾部插入方法return true;}/*** 根據元素值刪除節點(接口方法)* @param o 待刪除的元素值* @return 刪除成功返回 true*/@Overridepublic boolean remove(Object o) {// 處理 null 值的情況if (o == null) {for (Node<E> x = first; x != null; x = x.next) {if (x.item == null) { // 找到值為 null 的節點unlink(x); // 刪除該節點return true;}}} else {// 處理非 null 值的情況for (Node<E> x = first; x != null; x = x.next) {if (o.equals(x.item)) { // 找到值匹配的節點unlink(x); // 刪除該節點return true;}}}return false; // 未找到匹配節點}/*** 內部刪除節點的方法* @param x 待刪除的節點* @return 被刪除節點的元素值*/private E unlink(Node<E> x) {final E element = x.item; // 保存節點值final Node<E> nextNode = x.next; // 保存后繼節點final Node<E> prevNode = x.prev; // 保存前驅節點// 更新前驅節點的后繼指針if (prevNode == null) {first = nextNode; // 若無前驅,刪除的是頭節點,更新頭節點} else {prevNode.next = nextNode; // 前驅節點的后繼指向后繼節點x.prev = null; // 斷開當前節點的前驅}// 更新后繼節點的前驅指針if (nextNode == null) {last = prevNode; // 若無比后繼,刪除的是尾節點,更新尾節點} else {nextNode.prev = prevNode; // 后繼節點的前驅指向前驅節點x.next = null; // 斷開當前節點的后繼}x.item = null; // 幫助垃圾回收size--; // 元素數量減 1return element; // 返回被刪除的元素值}/*** 根據索引獲取元素(接口方法)* @param index 元素索引* @return 對應位置的元素*/@Overridepublic E get(int index) {return node(index).item; // 先找到節點,再返回其值}/*** 根據索引查找節點(優化查找方向)* @param index 節點索引* @return 對應的節點*/Node<E> node(int index) {// 如果索引在前半部分,從頭部開始查找if (index < (size >> 1)) {Node<E> x = first;for (int i = 0; i < index; i++) {x = x.next; // 向后移動指針}return x;} else {// 如果索引在后半部分,從尾部開始查找Node<E> x = last;for (int i = size - 1; i > index; i--) {x = x.prev; // 向前移動指針}return x;}}// ============================ 輔助方法 ============================/*** 打印鏈表結構(調試用)*/public void printLinkList() {if (size == 0) {System.out.println("鏈表為空");return;}Node<E> temp = first;System.out.printf("鏈表名稱:%s,頭節點:%s,尾節點:%s,整體:", name, first.item, last.item);while (temp != null) {System.out.print(temp.item + " → ");temp = temp.next;}System.out.println("null");}// ============================ 內部節點類 ============================/*** 鏈表節點結構(靜態內部類)* @param <E> 節點存儲的元素類型*/protected static class Node<E> {E item; // 節點存儲的值Node<E> next; // 后繼節點引用Node<E> prev; // 前驅節點引用/*** 節點構造函數* @param prev 前驅節點* @param element 存儲的值* @param next 后繼節點*/public Node(Node<E> prev, E element, Node<E> next) {this.item = element;this.next = next;this.prev = prev;}}// ============================ Getter 方法 ============================/*** 獲取鏈表名稱* @return 鏈表名稱*/public String getName() {return name;}
}
業務鏈路BusinessLinkedList
public class BusinessLinkedList<T, D, R> extends LinkedList<ILogicHandler<T, D, R>> implements ILogicHandler<T, D, R>{public BusinessLinkedList(String name) {super(name);}@Overridepublic R apply(T requestParameter, D dynamicContext) throws Exception {Node<ILogicHandler<T, D, R>> current = this.first;do {ILogicHandler<T, D, R> item = current.item;R apply = item.apply(requestParameter, dynamicContext);if (null != apply) return apply;current = current.next;} while (null != current);return null;}}
業務鏈路ILogicHandler
public interface ILogicHandler<T, D, R> {default R next(T requestParameter, D dynamicContext) {return null;}R apply(T requestParameter, D dynamicContext) throws Exception;}
?鏈路裝配:
public class LinkArmory<T, D, R> {private final BusinessLinkedList<T, D, R> logicLink;@SafeVarargspublic LinkArmory(String linkName, ILogicHandler<T, D, R>... logicHandlers) {logicLink = new BusinessLinkedList<>(linkName);for (ILogicHandler<T, D, R> logicHandler: logicHandlers){logicLink.add(logicHandler);}}public BusinessLinkedList<T, D, R> getLogicLink() {return logicLink;}}
初始化
首先有一個責任鏈工廠:
demo01是假設有兩個節點的責任鏈,demo2是假設只有一個節點的責任鏈,
@Service
public class Rule02TradeRuleFactory {@Bean("demo01")public BusinessLinkedList<String, DynamicContext, XxxResponse> demo01(RuleLogic201 ruleLogic201, RuleLogic202 ruleLogic202) {LinkArmory<String, DynamicContext, XxxResponse> linkArmory = new LinkArmory<>("demo01", ruleLogic201, ruleLogic202);return linkArmory.getLogicLink();}@Bean("demo02")public BusinessLinkedList<String, DynamicContext, XxxResponse> demo02(RuleLogic202 ruleLogic202) {LinkArmory<String, DynamicContext, XxxResponse> linkArmory = new LinkArmory<>("demo02", ruleLogic202);return linkArmory.getLogicLink();}@Data@Builder@AllArgsConstructor@NoArgsConstructorpublic static class DynamicContext {private String age;}}//在這里接受上面@Bean注解的注入,并且完成裝配鏈表的工作
public class LinkArmory<T, D, R> {private final BusinessLinkedList<T, D, R> logicLink;@SafeVarargspublic LinkArmory(String linkName, ILogicHandler<T, D, R>... logicHandlers) {logicLink = new BusinessLinkedList<>(linkName);for (ILogicHandler<T, D, R> logicHandler: logicHandlers){logicLink.add(logicHandler);}}public BusinessLinkedList<T, D, R> getLogicLink() {return logicLink;}}
測試方法:
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest
public class Link02Test {@Resource(name = "demo01")private BusinessLinkedList<String, Rule02TradeRuleFactory.DynamicContext, XxxResponse> businessLinkedList01;@Resource(name = "demo02")private BusinessLinkedList<String, Rule02TradeRuleFactory.DynamicContext, XxxResponse> businessLinkedList02;@Testpublic void test_model02_01() throws Exception {XxxResponse apply = businessLinkedList01.apply("123", new Rule02TradeRuleFactory.DynamicContext());log.info("測試結果:{}", JSON.toJSONString(apply));}@Testpublic void test_model02_02() throws Exception {XxxResponse apply = businessLinkedList02.apply("123", new Rule02TradeRuleFactory.DynamicContext());log.info("測試結果:{}", JSON.toJSONString(apply));}}
執行流程:兩只節點的情況:
初始化責任鏈
在工廠里裝配demo01的兩個節點,
@Bean("demo01")public BusinessLinkedList<String, DynamicContext, XxxResponse> demo01(RuleLogic201 ruleLogic201, RuleLogic202 ruleLogic202) {LinkArmory<String, DynamicContext, XxxResponse> linkArmory = new LinkArmory<>("demo01", ruleLogic201, ruleLogic202);return linkArmory.getLogicLink();}
進入LinkArmory的構造方法:遍歷節點并添加:
執行linkArmory.getLogicLink(); 獲取這個責任鏈:
執行測試方法
從這里獲取注入的bean,獲取到責任鏈
@Resource(name = "demo01")private BusinessLinkedList<String, Rule02TradeRuleFactory.DynamicContext, XxxResponse> businessLinkedList01;@Testpublic void test_model02_01() throws Exception {XxxResponse apply = businessLinkedList01.apply("123", new Rule02TradeRuleFactory.DynamicContext());log.info("測試結果:{}", JSON.toJSONString(apply));}
執行BusinessLinkedListz中的apply方法,就是遍歷責任鏈,挨個執行apply方法,直到執行到最后有返回值的時候就停止:
然后會執行各個節點的apply方法,并且去往下一個節點
在ILogicHandler的next是直接返回null的,然后再經過判斷:
default R next(T requestParameter, D dynamicContext) {return null;}
返回的是null就會繼續遍歷下一個節點:如果不是null就會結束,并把返回值返回。