责任链设计模式(单例+多例)
目录
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就会结束,并把返回值返回。