跳转至

责任链抽象模板设计分析 (基于 Git Diff 2.9 - 2.10)

本文档基于提供的 git diff 文件,分析其中实现的责任链模式的两种模型,并阐述其相关知识。

责任链模式 (Chain of Responsibility Pattern)

作用与意图

责任链模式是一种行为设计模式,它允许你将请求沿着处理者链进行发送。收到请求后,每个处理者均可对请求进行处理,或将其传递给链上的下一个处理者。

核心思想

  • 解耦请求发送者和接收者:发送者不需要知道哪个对象会处理其请求,链中的对象也不需要知道发送者的细节。
  • 动态组合责任:可以在运行时动态地组合链中的处理者,或者修改它们的顺序。
  • 请求逐级处理:请求在链上传递,直到有一个处理者处理它为止。

模型设计

  • 如图,这是一种多实例对象责任链的设计结构,会使用到如 Java JDK 源码中 Link 的方式填写链路,之后再有业务链路处理链路执行。而每一个链路都会被填充一个逻辑处理器的实现类(ILogicHandler)来处理具体的业务。
  • 那么,这样就很好的扩展了各种链路的使用诉求。

适用场景

  • 当程序需要使用不同方式处理不同种类请求, 而且请求类型和顺序预先未知时。
  • 当必须按顺序执行多个处理器时。
  • 当所需处理器集合及其顺序仅在运行时可用时。

代码实现分析

Git diff 中展示了两种责任链(或类似链路处理)的实现模型,分别位于 model1model2 相关的代码中。

Model 1: 单例责任链 (Singleton Chain of Responsibility)

这种模型更接近传统的责任链定义,通过显式链接构建处理者链条。

核心接口与抽象类

  • ILogicLink<T, D, R>: 定义链中单个节点的行为接口。它继承自 ILogicChainArmory,并增加了 apply 方法用于处理请求。
  • 文件路径: doppler-group-buy-market-types/src/main/java/site/dopplerxd/types/designs/framework/link/model1/ILogicLink.java
  • ILogicChainArmory<T, D, R>: 定义链的装配能力,主要包含 next()appendNext(ILogicLink<T, D, R> next) 方法。
  • 文件路径: doppler-group-buy-market-types/src/main/java/site/dopplerxd/types/designs/framework/link/model1/ILogicChainArmory.java
  • AbstractLogicLink<T, D, R>: ILogicLink 的抽象实现。它管理对下一个节点的引用 (private ILogicLink<T, D, R> next;),并提供了将请求传递给下一个节点的 protected R next(T requestParameter, D dynamicContext) 方法。
  • 文件路径: doppler-group-buy-market-types/src/main/java/site/dopplerxd/types/designs/framework/link/model1/AbstractLogicLink.java

构建链

通过 appendNext 方法将各个逻辑节点(实现了 ILogicLink 的类,如 RuleLogic101, RuleLogic102)手动连接起来形成责任链。

Java
1
2
3
4
5
// 示例来自 Rule01TradeRuleFactory.java
public ILogicLink<String, Rule02TradeRuleFactory.DynamicContext, String> openLogicLink() {
    ruleLogic101.appendNext(ruleLogic102);
    return ruleLogic101; // 返回链的头节点
}

请求处理流程

  1. 请求从链的头节点(如 ruleLogic101)开始。
  2. 当前节点的 apply 方法被调用。节点可以处理请求,或者通过调用其父类 AbstractLogicLinknext(requestParameter, dynamicContext) 方法将请求传递给链中的下一个节点。
  3. 如果 next() 方法被调用,它会调用下一个链接节点的 apply 方法。
  4. 这个过程持续进行,直到请求被某个节点完全处理(可能不再调用 next() 并返回结果),或者到达链的末尾。
Java
// 示例: RuleLogic101.java
@Override
public String apply(String requestParameter, Rule02TradeRuleFactory.DynamicContext dynamicContext) throws Exception {
    log.info("link model01 RuleLogic101");
    return next(requestParameter, dynamicContext); // 传递给下一个节点
}

// 示例: RuleLogic102.java (链的末端)
@Override
public String apply(String requestParameter, Rule02TradeRuleFactory.DynamicContext dynamicContext) throws Exception {
    log.info("link model01 RuleLogic102");
    return "link model01 单实例链"; // 处理并返回结果
}

相关文件

  • doppler-group-buy-market-types/src/main/java/site/dopplerxd/types/designs/framework/link/model1/ILogicLink.java
  • doppler-group-buy-market-types/src/main/java/site/dopplerxd/types/designs/framework/link/model1/ILogicChainArmory.java
  • doppler-group-buy-market-types/src/main/java/site/dopplerxd/types/designs/framework/link/model1/AbstractLogicLink.java
  • doppler-group-buy-market-app/src/test/java/site/dopplerxd/test/types/rule01/factory/Rule01TradeRuleFactory.java
  • doppler-group-buy-market-app/src/test/java/site/dopplerxd/test/types/rule01/logic/RuleLogic101.java
  • doppler-group-buy-market-app/src/test/java/site/dopplerxd/test/types/rule01/logic/RuleLogic102.java

这种模型使用一个集合(自定义链表)来管理一系列独立的处理器。链的执行由该集合控制,依次调用每个处理器的处理方法。

核心接口与类

  • ILogicHandler<T, D, R>: 定义链中处理单元的接口。核心方法是 apply(T requestParameter, D dynamicContext)
  • (此接口定义未在 diff 中直接显示,但其使用贯穿 Model 2 的实现)
  • LinkedList<E>ILink<E>: 通用的双向链表数据结构及其接口,用于存储 ILogicHandler 实例。
  • 文件路径: doppler-group-buy-market-types/src/main/java/site/dopplerxd/types/designs/framework/link/model2/chain/LinkedList.java
  • 文件路径: doppler-group-buy-market-types/src/main/java/site/dopplerxd/types/designs/framework/link/model2/chain/ILink.java
  • BusinessLinkedList<T, D, R>: 继承自 LinkedList<ILogicHandler<T, D, R>>,并且自身也实现了 ILogicHandler<T, D, R> 接口。它代表一个可执行的业务链路,内部按顺序存储和执行一系列 ILogicHandler
  • 文件路径: doppler-group-buy-market-types/src/main/java/site/dopplerxd/types/designs/framework/link/model2/chain/BusinessLinkedList.java
  • LinkArmory<T, D, R>: 一个工厂或装配类,用于方便地创建和初始化 BusinessLinkedList,将多个 ILogicHandler 实例添加到链中。
  • 文件路径: doppler-group-buy-market-types/src/main/java/site/dopplerxd/types/designs/framework/link/model2/LinkArmory.java

构建链

LinkArmory 在其构造函数中接收一个或多个 ILogicHandler 实例,并将它们添加到内部创建的 BusinessLinkedList 中。

Java
1
2
3
4
5
6
// 示例来自 Rule02TradeRuleFactory.java
@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();
}

请求处理流程

  1. BusinessLinkedListapply 方法被调用时,它会遍历其内部存储的 ILogicHandler 节点(从 first 节点开始)。
  2. 对于链表中的每个 ILogicHandler (item),调用其 apply 方法。
  3. 如果某个 item.apply(...) 返回了一个非 null 的结果,BusinessLinkedList 会立即返回这个结果,后续的处理器将不再执行。
  4. 如果遍历完所有处理器,都没有返回非 null 的结果,则 BusinessLinkedListapply 方法返回 null
Java
// BusinessLinkedList.java 的 apply 方法片段
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 (apply != null) {
            return apply; // 一旦有处理器返回结果,则中断并返回
        }
        current = current.next;
    } while (current != null);
    return null; // 所有处理器均未返回有效结果
}
  • 关于 ILogicHandler 内部的 next() 调用: 在 RuleLogic201.java 中,其 apply 方法调用了 next(requestParameter, dynamicContext)。如果 ILogicHandler 接口本身没有定义 next 方法(或默认实现),这可能是一个未完成的特性或特定于 RuleLogic201 的内部逻辑。BusinessLinkedList 的主要迭代机制是通过遍历其节点列表,而不是依赖处理器内部的 next() 调用来驱动到 列表中的下一个处理器
Java
// 示例: RuleLogic201.java
public XxxResponse apply(String requestParameter, Rule02TradeRuleFactory.DynamicContext dynamicContext) throws Exception{
    log.info("link model02 RuleLogic201");
    return next(requestParameter, dynamicContext); // 此 next 的行为取决于 ILogicHandler 或其父类/接口的定义
}

// 示例: RuleLogic202.java
public XxxResponse apply(String requestParameter, Rule02TradeRuleFactory.DynamicContext dynamicContext) throws Exception{
    log.info("link model02 RuleLogic202");
    return new XxxResponse("hi 小傅哥!"); // 处理并返回结果
}

相关文件

  • doppler-group-buy-market-types/src/main/java/site/dopplerxd/types/designs/framework/link/model2/handler/ILogicHandler.java (推断)
  • doppler-group-buy-market-types/src/main/java/site/dopplerxd/types/designs/framework/link/model2/LinkArmory.java
  • doppler-group-buy-market-types/src/main/java/site/dopplerxd/types/designs/framework/link/model2/chain/BusinessLinkedList.java
  • doppler-group-buy-market-types/src/main/java/site/dopplerxd/types/designs/framework/link/model2/chain/ILink.java
  • doppler-group-buy-market-types/src/main/java/site/dopplerxd/types/designs/framework/link/model2/chain/LinkedList.java
  • doppler-group-buy-market-app/src/test/java/site/dopplerxd/test/types/rule02/factory/Rule02TradeRuleFactory.java
  • doppler-group-buy-market-app/src/test/java/site/dopplerxd/test/types/rule02/logic/RuleLogic201.java
  • doppler-group-buy-market-app/src/test/java/site/dopplerxd/test/types/rule02/logic/RuleLogic202.java
  • doppler-group-buy-market-app/src/test/java/site/dopplerxd/test/types/rule02/logic/XxxResponse.java

总结与对比

  • Model 1 (单例责任链):
  • 结构:处理者之间通过 next 指针显式链接,形成单向链。
  • 控制流:由当前处理者决定是否处理以及是否传递给下一个处理者。
  • 灵活性:链的构建相对固定,但可以在抽象类中加入更多通用逻辑。

  • Model 2 (业务链路/多实例处理器链):

  • 结构:处理者作为独立对象存在,由一个外部容器(BusinessLinkedList)按顺序管理。
  • 控制流:由容器(BusinessLinkedList)迭代调用每个处理者的 apply 方法。一旦某个处理器返回有效结果,链式调用通常会中止。
  • 灵活性:更容易动态增删处理器(通过修改 BusinessLinkedList 的内容)。处理器之间耦合度更低,它们不需要知道彼此的存在,只需实现 ILogicHandler 接口。

两种模型都旨在将一系列处理步骤解耦并有序地组织起来。Model 1 是经典的责任链实现,而 Model 2 更像是一个基于列表的处理器调度或组合模式,提供了另一种构建有序处理流程的思路,尤其适用于处理器返回结果即表示处理完成的场景。