职责链模式

说给前面

正式入职京东快一个月了,加上过往的实习不知不觉已经进入职场半年了,幸运的是目前所在的团队很nice,生活也开始走向了正轨,一路走来满是艰辛,22岁的现在终于可以释怀18岁的自己了,人生没有白走的路,每一步都算数!There is more to come!

一些定义

职责链模式的英文翻译是

Chain Of Responsibility Design Pattern。

在 GoF 的《设计模式》中,它是这么定义的:

Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving
objects and pass the request along the chain until an object handles it.

翻译成中文就是:将请求的发送和接收解耦,让多个接收对象都有机会处理这个请求。将这些接收对象串成一条链,并沿着这条链传递这个请求,直到链上的某个接收对象能够处理它为止。这么说比较抽象,我用更加容易理解的话来进一步解读一下。在职责链模式中,多个处理器(也就是刚刚定义中说的“接收对象”)依次处理同一个请求。一个请求先经过 A 处理器处理,然后再把请求传递给 B 处理器,B 处理器处理完后再传递给 C 处理器,以此类推,形成一个链条。链条上的每个处理器各自承担各自的处理职责,所以叫作职责链模式

最佳实践

  • 标准的职责链模式,链上的处理器顺序执行,有一个处理器可以处理,就终止传递执行
  • 变体的职责链模式,链上的处理器会顺序执行,不会终止。

这里举一个实际中的例子,对于支持 UGC(User Generated Content,用户生成内容)的应用(比如论坛)来说,用户生成的内容(比如,在论坛中发表的帖子)可能会包含一些敏感词(比如涉黄、广告、反动等词汇)。针对这个应用场景,我们就可以利用职责链模式来过滤这些敏感词。对于包含敏感词的内容,我们有两种处理方式,一种是直接禁止发布,另一种是给敏感词打马赛克(比如,用 * 替换敏感词)之后再发布。实现这一套敏感词算法过滤就可以用到变体的职责链模式,链上的处理器都会执行,并且可以在不修改源码的情况下增加新的过滤器。

Coding

在实际业务场景中,变体的职责链更通用,实现方式主要有两种,一种是利用链表存储节点,另一种则是利用数组存储节点,这里我选取了单链表作为节点存储的媒介

public abstract class AbstractChain {
    /**
     * 指向下一节点
     */
    protected AbstractChain next;

    /**
     * 顺序
     */
    private int sort;

    public int getSort() {
        return sort;
    }

    public void setSort() {
        this.sort = this.initSort();
    }

    /**
     * 保存节点,链式调用
     *
     * @param next
     * @return
     */
    public void next(AbstractChain next) {
        this.next = next;
    }

    /**
     * 初始化节点顺序
     *
     * @return
     */
    protected abstract int initSort();

    /**
     * 抽象方法执行
     *
     * @return
     */
    public abstract boolean excute();

    public boolean doExcute() {
        boolean excute = this.excute();
        if (Boolean.TRUE.equals(excute) && this.next != null) {
            excute = this.next.doExcute();
        }
        return excute;
    }
}

基础链式节点主要包含节点的初始化与抽象方法的执行器,这里我增加了一个排序字段,可以手动控制我们节点的执行顺序

构造链工厂,主要实现节点的追加扩展以及提供链表的头尾节点

public class ChainBuilder {
    /**
     * 头结点
     */
    private AbstractChain head;
    /**
     * 尾结点
     */
    private AbstractChain tail;

    /**
     * 追加结点
     * @param chain
     * @return
     */
    public ChainBuilder addChain(AbstractChain chain) {
        if (this.head == null) {
            this.head = this.tail = chain;
            return this;
        }
        this.tail.next(chain);
        this.tail = chain;
        return this;
    }

    /**
     * 返回头结点
     * @return
     */
    public AbstractChain build(){
        return this.head;
    }

    /**
     * 返回尾结点
     * @return
     */
    public AbstractChain tail(){
        return this.tail;
    }
}

职责链模式的核心步骤,责任链的初始化以及执行

@Component
public class BuildComponent implements ApplicationContextAware {
    /**
     * 头节点
     */
    private AbstractChain head;

    public boolean execute() {
        try {
            return head.doExcute();
        } catch (Exception e) {
            log.error("责任链执行异常:", e);
            return true;
        }
    }

    /**
     * 责任链初始化
     *
     * @param applicationContext
     * @throws BeansException
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("开始初始化责任链");
        Map<String, AbstractChain> beanMap = applicationContext.getBeansOfType(AbstractChain.class);
        List<AbstractChain> chainList = new ArrayList<>();
        for (String name : beanMap.keySet()) {
            AbstractChain chain = beanMap.get(name);
            chain.setSort();
            chainList.add(chain);
        }

        //升序排序
        chainList.sort(Comparator.comparing(AbstractChain::getSort));

        //构建链
        ChainBuilder builder = new ChainBuilder();
        for (AbstractChain chain : chainList) {
            builder.addChain(chain);
        }
        head = builder.build();
        System.out.println("活动链初始化完成");
    }
}

这里模拟了三个执行链节点,不涉及任何的业务逻辑,目的是能让我们更为简洁的看到职责链的执行效果

@Component
public class ValidateComponentOne extends AbstractChain {
    @Override
    protected int initSort() {
        return 100;
    }

    @Override
    public boolean excute() {
        System.out.println("ValidateComponentOne");
        return true;
    }
}
@Component
public class ValidateComponentTwo extends AbstractChain {
    @Override
    protected int initSort() {
        return 200;
    }

    @Override
    public boolean excute() {
        System.out.println("ValidateComponentTwo");
        return true;
    }
}
@Component
public class ValidateComponentThree extends AbstractChain {
    @Override
    protected int initSort() {
        return 300;
    }
    
    @Override
    public boolean excute() {
        System.out.println("ValidateComponentThree");
        return true;
    }
}

在Spring环境下编写一个测试类来执行我们的职责链

    @Test
    public void component(){
        buildComponent.execute();
    }
开始初始化责任链
活动链初始化完成
2022-07-21 21:08:39.947  INFO 688 --- [           main] s.k.chain.SpringBootApplicationTests     : Started SpringBootApplicationTests in 3.531 seconds (JVM running for 5.268)
ValidateComponentOne
ValidateComponentTwo
ValidateComponentThree

我们发现使用了职责链模式进行代码重构,以往执行一套校验或者过滤逻辑需要大篇幅的判断和处理,现在只需要调用职责链的执行方法就可以按照我们编写的逻辑节点一个个执行下去,并且后续需要增加校验或者过滤逻辑,只需要增加一个继承节点即可。

业务逻辑专注于业务,其他的步骤可以适当交给责任链即可,随着不同的业务逻辑可以适当改造责任链,使其更适配我们的业务

最后修改:2022 年 07 月 21 日
如果觉得我的文章对你有用,请随意赞赏