关于java:轻松搞懂设计模式大厂都在使用的责任链模式

54次阅读

共计 6051 个字符,预计需要花费 16 分钟才能阅读完成。

我是「kangarooking(袋鼠帝)」,不是弟弟的弟。一个互联网小蜗「gua」牛,陪大家一起一步一步往上爬,总有一天有属于咱们的天。关注 me,每天提高亿点点 ❗ ❗ ❗

前言(忽悠)

之前浏览了一些开源框架的源码,发现责任链模式就像一个 xxx(此处词穷, 大佬们评论区帮我填词 0.0)一样,利用在各大框架中。并且有多种实现形式,上面的内容好好看,好好学。面试时,让你能跟面试官多过几招,工作中,让领导对你另眼相看。

初识

责任链:次要用于流程管制和解决,像销假的审批流程,工厂外面的流水线,devops 的 pipeline(流水线)等,都能够看做责任链。

简介(鬼话)


责任链(Chain of Responsibility)模式的定义:为了防止申请发送者与多个申请解决者耦合在一起,于是将所有申请的解决者通过前一对象记住其下一个对象的援用而连成一条链;当有申请产生时,可将申请沿着这条链传递,直到有对象解决它为止。

在责任链模式中,客户只须要将申请发送到责任链上即可,毋庸关怀申请的解决细节和申请的传递过程,申请会主动进行传递。所以责任链将申请的发送者和申请的解决者解耦了。

责任链模式是一种对象行为型模式,其次要 长处 如下。

  • 升高了对象之间的耦合度。该模式使得一个对象毋庸晓得到底是哪一个对象解决其申请以及链的构造,发送者和接收者也毋庸领有对方的明确信息。
  • 加强了零碎的可扩展性。能够依据须要减少新的申请解决类,满足开闭准则。
  • 加强了给对象指派职责的灵活性。当工作流程发生变化,能够动静地扭转链内的成员或者调动它们的秩序,也可动静地新增或者删除责任。
  • 责任链简化了对象之间的连贯。每个对象只需放弃一个指向其后继者的援用,不需放弃其余所有解决者的援用,这防止了应用泛滥的 if 或者 if···else 语句。
  • 责任分担。每个类只须要解决本人该解决的工作,不该解决的传递给下一个对象实现,明确各类的责任范畴,合乎类的繁多职责准则。

其次要 毛病 如下。

  • 不能保障每个申请肯定被解决。因为一个申请没有明确的接收者,所以不能保障它肯定会被解决,该申请可能始终传到链的末端都得不到解决。
  • 对比拟长的职责链,申请的解决可能波及多个解决对象,零碎性能将受到肯定影响。
  • 职责链建设的合理性要靠客户端来保障,减少了客户端的复杂性,可能会因为职责链的谬误设置而导致系统出错,如可能会造成循环调用。

援用自:http://c.biancheng.net/view/1…

目标:将申请方与解决方解耦,链式调用,能够灵便扭转解决程序,合乎开闭准则,可扩展性强。

从理论登程(人话)

场景:公司要实现一个流程审批的性能,如销假流程审批,加班审批,报销审批等。

三个因素:

  1. 责任链(审批流程)
  2. 解决人(审批人)
  3. 链中传递的信息(审批内容)

留神:对可能扩大的因素做形象解决

代码示例

代码地址:https://gitee.com/kangarookin…

定义审批人的抽象类,「设计模式的习用套路,将可能有多个实现的类定义为形象,进步程序 扩展性」,本示例中审批人可能会有 HR,经理,技术总监,小组长等等 …

/**
 * 责任链中的节点(流程中的审批人)* @param <T>
 * 这里将审批的信息定义为泛型,更灵便
 */
@Data
public abstract class ApplyHandler<T> {
    protected ApplyHandler<?> next = null;
    // 定义该审批人在整个责任链中的审批程序
    protected Integer order;
    protected String name;

    public void apply(Object obj) {if (null != next) {next.transform(obj);
        }else {System.out.println("-----------------------------");
            System.out.println("end");
        }
    }

    @SuppressWarnings("unchecked")
    public void transform(Object obj){
        // 将传入的对象转换为泛型定义的类型
        T param = (T)obj;
        handle(param);
    }

    // 具体的审批逻辑
    protected abstract void handle(T param);

    public ApplyHandler(Integer order) {this.order = order;}

    public ApplyHandler() {}
}

创立形象责任链(审批流程)并创立一个默认链(默认审批流程)。为前面创立的其余类型的审批流程提供一个可扩大的口子。

/**
 * 责任链的管理者
 * 治理链头尾,以及链条的增删改查
 */
@Data
public abstract class ApplyChain {public ApplyHandler<?> first = new ApplyHandler<Object>() {
        @Override
        public void handle(Object obj) {
            // 第一个节点不参加解决,间接往下申请
            apply(obj);
        }
    };
    private ApplyHandler<?> end = first;

    // 责任链的调用入口
    protected abstract void exc(Object obj);

    // 向尾部增加节点
    public void addLast(ApplyHandler<?> handler){end.setNext(handler);
        end = handler;
    }

    //... 此处省略了其余操作责任链的办法
}

/**
 * 默认审批流程
 */
class DefaultApplyChain extends ApplyChain {
    @Override
    protected void exc(Object obj) {
        // 应用链头开始调用整条责任链
        System.out.println("--------------- start --------------");
        first.transform(obj);
    }
}

定义不同的理论审批人

public class GroupLeaderHandler extends ApplyHandler<LeaveApplyMsg> {public GroupLeaderHandler(Integer order) {super(order);
    }
    @Override
    protected void handle(LeaveApplyMsg applyMsg) {System.out.println("小组长审批");
        // 审批实现后持续向下申请
        apply(applyMsg);
    }
}

class ManagerHandler extends ApplyHandler<LeaveApplyMsg> {public ManagerHandler(Integer order) {super(order);
    }
    @Override
    protected void handle(LeaveApplyMsg applyMsg) {System.out.println("经理审批");
        // 审批实现后持续向下申请
        apply(applyMsg);
    }
}

class HrHandler extends ApplyHandler<LeaveApplyMsg> {public HrHandler(Integer order) {super(order);
    }
    @Override
    protected void handle(LeaveApplyMsg applyMsg) {System.out.println("HR 审批");
        // 审批实现后持续向下申请
        apply(applyMsg);
    }
}

定义销假信息

/**
 * 销假审批信息
 */
@Data
public class LeaveApplyMsg {
    private String msg;
    private Integer level;
    private String name;
    private Integer hour;
}

模仿用户提交销假申请

public class ChainClient {public static void main(String[] args) {
        // 该 list 能够从数据库中的某个表查问进去
        List<ApplyHandler<LeaveApplyMsg>> applyHandlerList = new ArrayList<>();
        ApplyHandler<LeaveApplyMsg> hr = new HrHandler(3);
        ApplyHandler<LeaveApplyMsg> gl = new GroupLeaderHandler(1);
        ApplyHandler<LeaveApplyMsg> m = new ManagerHandler(2);
        applyHandlerList.add(m);
        applyHandlerList.add(gl);
        applyHandlerList.add(hr);
        // 依据 order 字段排序
        List<ApplyHandler<LeaveApplyMsg>> collect = applyHandlerList.stream().sorted(Comparator.comparing(ApplyHandler::getOrder)).collect(Collectors.toList());
        ApplyChain applyChain = new DefaultApplyChain();
        // 循环的组装到责任链中
        for (ApplyHandler<?> applyHandler : collect) {applyChain.addLast(applyHandler);
        }
        // 在理论场景中,下面的代码能够放到系统启动的时候初始化数据的办法中,spring 有很多这样的扩大点,能够自行理解

        // 这上面的代码就相当于,用户在前端抉择好审批类型(对应不同的信息),而后点击申请
        // 后端就触发 applyChain.exc(applyMsg); 这个办法开始执行整个责任链
        LeaveApplyMsg applyMsg = new LeaveApplyMsg();
        applyMsg.setMsg("apply leave");
        applyMsg.setName("lhm");
        applyMsg.setLevel(1);
        applyChain.exc(applyMsg);

    }
}

注:以上的责任链写法参考 sentinel 责任链应用形式(单向链表)。当然也能够应用 list 来搞。

开源框架中责任链的应用

spring-cloud-gateway中申请过滤就应用了责任链模式,入口在 FilteringWebHandler 类中。
shenyu网关中各个插件的调用也应用了责任链模式,入口在 ShenyuWebHandler 类中。
sentinel 中对各个 slot 插槽的调用应用责任链模式,入口可查看 CtSph#asyncEntryWithPriorityInternal 办法的上面这行代码

小伙伴们有趣味能够去看看这些框架中是如何实现责任链模式的。看懂示例代码很容易搞懂 sentinel 的应用形式,gateway 和 shenyu 都是网关,他们实现责任链的形式相似,应用的是响应式编程,咱们能够看看实现思路。

上面是 spring-cloud-gateway 的实现形式

private static class DefaultGatewayFilterChain implements GatewayFilterChain {
        // 管制以后应用列表中哪个 filter 解决
        private final int index;
        // 寄存所有的 filter(也就是这种实现形式采纳 list 作为责任链)private final List<GatewayFilter> filters;

        DefaultGatewayFilterChain(List<GatewayFilter> filters) {
            this.filters = filters;
            this.index = 0;
        }

        private DefaultGatewayFilterChain(DefaultGatewayFilterChain parent, int index) {this.filters = parent.getFilters();
            this.index = index;
        }

        public List<GatewayFilter> getFilters() {return filters;}
        
        // 链式调用的入口
        @Override
        public Mono<Void> filter(ServerWebExchange exchange) {return Mono.defer(() -> {
                // 判断是否调用实现
                if (this.index < filters.size()) {
                    // 依据 index 获取以后须要调用的节点
                    GatewayFilter filter = filters.get(this.index);
                   // 创立新的 chain
                   DefaultGatewayFilterChain chain = new DefaultGatewayFilterChain(this,
                            this.index + 1);
                    // 调用 filter 理论解决逻辑
                    return filter.filter(exchange, chain);
                }
                else {return Mono.empty(); // complete
                }
            });
        }

    }

查看其中一个 GatewayFilter 实现类的 filter 逻辑 (该代码位于StripPrefixGatewayFilterFactory 类中)

能够看到通过调用 filter.filter(exchange, chain); 办法解决理论的 filter 逻辑,在最初又会调用 chain.filter 办法回到链式调用的入口处。这样就产生了循环调用,通过扭转 index 的值,挨个获取 list 中的 filter 对象,这样就实现了对 filters 列表中各 filter 的链式调用。

总结

责任链模式在很多框架都有利用,学懂该模式,有助于对框架源码的了解,当业务有需要的时候,依据理论状况也可思考引入责任链模式。不难看出设计模式只是一种思维并没有一个固定写法。所以不同的框架可能有不同的实现形式(依据理论我的项目状况来定)。心愿看完这篇文章的小伙伴也能够有本人的思考,实现一个属于你本人写法的责任链模式吧!

学习设计模式的一点领会:最开始学习设计模式的时候总感觉每次学懂了,也本人写了代码,然而很快就会遗记了,最初还是一个你不意识他,他也不意识你的状态,而且有些设计模式很容易搞混。那应该怎么样能力测地把握各种设计模式呢???好的,我是 kangrooking,关注我,欲知后事如何请听下集分晓 0_0

目前轻松搞懂设计模式也出了两期了,我还在一直的改良和学习中,既要让兄弟们能有趣味看完,又要让让内容通俗易懂而又不失深度,属实是有点艰难,这篇也是修修改改了好多版,心愿兄弟们在评论区提点贵重的意见啊。

微信公众号「袋鼠学生的客栈」,你们的三连,是我的饲料~~。点赞👍 关注❤️ 分享👥

正文完
 0