Skip to content

责任链

这在订单中使用。如果你一下子去看订单里的责任链,可能会很懵。没关系,接下来我们将通过一个示例来讲解如何在项目里运用责任链。

定义

顾名思义,责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式。 在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。

简单来说就是职责链上的处理者负责处理请求,客户只需要将请求发送到职责链上即可,无须关心请求的处理细节和请求的传递。

什么时候使用它

当某块业务逻辑既要又要还要且还怎样时,每个逻辑有明显的判断逻辑。这时候考虑使用它。

优缺点

优点:

  • 降低耦合度。它将请求的发送者和接收者解耦。
  • 简化了对象。使得对象不需要知道链的结构。
  • 增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除。
  • 增加新的请求处理类很方便。

缺点:

  • 不能保证请求一定被接收。
  • 系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用。
  • 可能不容易观察运行时的特征,有碍于除错。

核心实现

  • 抽象处理者(Handler)

定义一个处理请求的接口,通常包含一个处理请求的方法(如 handleRequest)和一个指向下一个处理者的引用(后继者)。

  • 具体处理者(ConcreteHandler)

实现了抽象处理者接口,负责处理请求。如果能够处理该请求,则直接处理;否则,将请求传递给下一个处理者。

  • 客户端(Client)

创建处理者对象,并将它们连接成一条责任链。通常,客户端只需要将请求发送给责任链的第一个处理者,无需关心请求的具体处理过程。

示例

需求是,促销某个业务时,需要发送一条消息到站内消息、订阅微信通知的用户、APP推送、短信其他等等。

像这种需求就是“既要又要还要且还怎样时”

想象一下,如果不使用任何设计模式,是不是可以很轻松的写出如下代码:

java

private final MessageTemplateService messageTemplateService;

public void send(Message message) {
     MessageTemplate messageTemplate = messageTemplateService.get();
    if(messageTemplate.enable(站内消息)) {
        // 发送站内消息
    }

    if(messageTemplate.enable(发送微信通知)) {
       // 发送微信通知
    }

    if(messageTemplate.enable(发送APP)) {
      // 发送APP
    }

    if(messageTemplate.enable(发送短信)) {
      // 发送短信
    }
}

当然了,这种写法违背了各种开发原则。如果我们要使用责任链来优化上面的代码,我们又应该要怎么做呢?

为了更好的实现通用性,先定义一个抽象接口:

java
/**
 * 抽象业务责任链组件
 *
 * @author mjyang
 * @date 2023/5/23 17:03
 */
public interface AbstractChainHandler<T> extends Ordered {
    
    /**
     * 执行责任链逻辑
     *
     * @param t 责任链执行入参
     */
    void handler(T t);
    
    /**
     * 责任链组件标识
     *
     * @return 责任链组件标识
     */
    String mark();
}

注意 mark 方法标识,通用责任链将会通过这个标识来串起链。同时,需要注意的是,接口实现了 Ordered 接口,这个主要是用来给链排序使用。

先来定义一个通用的抽象处理者:

java
/**
 * 抽象责任链上下文
 *
 * @author mjyang
 * @date 2023/5/23 17:03
 */
@Component
public final class AbstractChainContext<T> implements CommandLineRunner {

    private final Map<String, List<AbstractChainHandler>> abstractChainHandlerContainer = MapUtil.newHashMap();

    /**
     * 责任链组件执行
     *
     * @param mark 责任链组件标识
     * @param t    请求参数
     */
    public void handler(String mark, T t) {
        List<AbstractChainHandler> abstractChainHandlers = abstractChainHandlerContainer.get(mark);
        if (CollectionUtil.isEmpty(abstractChainHandlers)) {
            throw new RuntimeException(String.format("%s chain of id is undefined.", mark));
        }
        abstractChainHandlers.forEach(handler -> handler.handler(t));
    }

    @Override
    public void run(String... args) {
        Map<String, AbstractChainHandler> chainFilterMap = SpringUtil.getBeansOfType(AbstractChainHandler.class);
        chainFilterMap.forEach((beanName, bean) -> {
            List<AbstractChainHandler> abstractChainHandlers = abstractChainHandlerContainer.get(bean.mark());
            if (CollectionUtils.isEmpty(abstractChainHandlers)) {
                abstractChainHandlers = new ArrayList<>();
            }
            abstractChainHandlers.add(bean);
            List<AbstractChainHandler> actualAbstractChainHandlers = abstractChainHandlers.stream()
                    .sorted(Comparator.comparing(Ordered::getOrder))
                    .collect(Collectors.toList());
            abstractChainHandlerContainer.put(bean.mark(), actualAbstractChainHandlers);
        });
    }
}

为了方便使用前面提到的 mark。我们可以先定义一个接口来表示:

java
/**
 * 消息发送计算链
 *
 * @author mjyang
 * @date 2024/2/23 10:17
 */
public interface MessagePushChainFilter<T extends MessagePushDto> extends AbstractChainHandler<T> {

    /**
     * 责任链组件标识
     *
     * @return 责任链组件标识
     */
    @Override
    default String mark() {
        return "message_push";
    }
}

再来定义具体链:

java
/**
 * 站内消息推送处理器
 *
 * @author mjyang
 * @date 2022/10/31 14:06
 */
@Component
@Slf4j
public class MessagePushSiteMessageHandler implements MessagePushChainFilter<MessagePushDto> {

    @Override
    public void handler(MessagePushDto context) {
        ...
    }


    @Override
    public int getOrder() {
        return 1;
    }
}
java
/**
 * 微信推送处理器
 *
 * @author mjyang
 * @date 2022/10/31 14:06
 */
@Component
@Slf4j
public class MessagePushWxMaHandler implements MessagePushChainFilter<MessagePushDto> {

    @Override
    public void handler(MessagePushDto context) {
        ...
    }


    @Override
    public int getOrder() {
        return 2;
    }
}

接下来就是客户端调用了:

java
/**
 * 消息推送应用服务类
 *
 * @author mjyang
 * @date 2023/6/27 14:45
 */
@Service
@RequiredArgsConstructor
public class MessagePushServiceImpl implements MessagePushService {

    private final MessageTemplateRepository messageTemplateRepository;
    private final AbstractChainContext<MessagePushDto> messagePushHandlerChain;

    @Override
    public void push(MessagePushDto dto) {
        MessageTemplate messageTemplate = this.messageTemplateRepository.get(dto.getNotifyBizType(), dto.getNotifyBizSubType());
        messagePushHandlerChain.handler("message_push", dto);
    }
}

通过这样的一个改造。我们将业务和发送逻辑相分离,后续如果需要在新增发送者,我们只需要在增加一个 messageHandler 即可,核心流程代码都不用改动。