当前位置: 首页 > news >正文

责任链设计模式(单例+多例)

目录

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;

    @Override
    public ILogicLink<T, D, R> next() {
        return next;
    }

    @Override
    public 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>{

    @Override
    public 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>{

    @Override
    public String apply(String requestParameter, Rule01TradeRuleFactory.DynamicContext dynamicContext) throws Exception {

        log.info("link model01 RuleLogic102");

        return "link model01 单实例链";
    }

}

Rule01TradeRuleFactory 类是一个工厂类,它的主要作用是创建和组装责任链。在这个责任链中,RuleLogic101 和 RuleLogic102 是具体的规则处理器,通过工厂类将它们连接成一个链,以便按顺序处理请求。

@Service
public class Rule01TradeRuleFactory {

    @Resource
    private RuleLogic101 ruleLogic101;
    @Resource
    private RuleLogic102 ruleLogic102;

    public ILogicLink<String, Rule02TradeRuleFactory.DynamicContext, String> openLogicLink() {
        ruleLogic101.appendNext(ruleLogic102);
        return ruleLogic101;
    }

    @Data
    @Builder
    @AllArgsConstructor
    @NoArgsConstructor
    public static class DynamicContext {
        private String age;
    }

}

测试方法

    @Test
    public 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); // 创建新节点,前驱为原尾节点,后继为 null
        last = newNode; // 更新尾节点为新节点

        // 若原尾节点为空(链表为空)
        if (oldLast == null) {
            first = newNode; // 同时更新头节点
        } else {
            oldLast.next = newNode; // 原尾节点的后继指向新节点
        }
        size++; // 元素数量加 1
    }

    // ============================ ILink 接口实现 ============================

    /**
     * 默认将元素添加到链表尾部(接口方法)
     * @param e 待添加的元素
     * @return 添加成功返回 true
     */
    @Override
    public boolean add(E e) {
        linkLast(e); // 调用尾部插入方法
        return true;
    }

    /**
     * 在链表头部添加元素(接口方法)
     * @param e 待添加的元素
     * @return 添加成功返回 true
     */
    @Override
    public boolean addFirst(E e) {
        linkFirst(e); // 调用头部插入方法
        return true;
    }

    /**
     * 在链表尾部添加元素(接口方法)
     * @param e 待添加的元素
     * @return 添加成功返回 true
     */
    @Override
    public boolean addLast(E e) {
        linkLast(e); // 调用尾部插入方法
        return true;
    }

    /**
     * 根据元素值删除节点(接口方法)
     * @param o 待删除的元素值
     * @return 删除成功返回 true
     */
    @Override
    public 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--; // 元素数量减 1
        return element; // 返回被删除的元素值
    }

    /**
     * 根据索引获取元素(接口方法)
     * @param index 元素索引
     * @return 对应位置的元素
     */
    @Override
    public 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);
    }

    @Override
    public 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;

    @SafeVarargs
    public 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
    @NoArgsConstructor
    public static class DynamicContext {
        private String age;
    }

}

//在这里接受上面@Bean注解的注入,并且完成装配链表的工作
public class LinkArmory<T, D, R> {

    private final BusinessLinkedList<T, D, R> logicLink;

    @SafeVarargs
    public 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;

    @Test
    public void test_model02_01() throws Exception {
        XxxResponse apply = businessLinkedList01.apply("123", new Rule02TradeRuleFactory.DynamicContext());
        log.info("测试结果:{}", JSON.toJSONString(apply));
    }

    @Test
    public 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;


   @Test
    public 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就会结束,并把返回值返回。

相关文章:

  • T5-LM 模型总结
  • go 通过汇编分析函数传参与返回值机制
  • NuGet包离线安装
  • Linux 入门七:从基础到进阶的文件操作
  • 【Ubutun】 在Linux Yocto的基础上去适配4G模块
  • 【AI提示词】创业导师提供个性化创业指导
  • JAVA后端八股面试经验总结-前言篇
  • TMS320F28P550SJ9学习笔记14:EPWM_死区dead_baund
  • UE4 踩坑记录
  • 电脑提示“找不到mfc140u.dll“的完整解决方案:从原因分析到彻底修复
  • 【毕设通关】——文献查阅
  • 什么是iPaaS?
  • 【vue3】@click函数传动态变量参数
  • kubernetes》》k8s》》Volume 数据卷 PVC PV NFS
  • 7# 5多线-7 不会停
  • 【Python语言基础】18、多态
  • 数据库管理-第313期 分布式挑战单机,OceanBase单机版试玩(20250411)
  • qt联动其他库实现一个客户端(本章主要是概述如何实现)
  • Python代码相关关系矩阵的三种展示热力图-条形图
  • C语言之双层for循环
  • 如何用书签 做网站接口/爱站网长尾关键词挖掘工具
  • 网站开发编程/推广app用什么平台比较好
  • 北京微信网站制作电话/服务营销7p理论
  • 临沂网站建设网站推广/外链网盘网站
  • 江门有什么网站推广/3步打造seo推广方案
  • 黄山家居网站建设怎么样/软文营销案例文章