责任链模式在项目中的应用

责任链模式定义

责任链模式是一种设计模式。在责任链模式里,由每一个对象的引用而连接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织和分配责任。这么说可能有些不好理解,我们举个生活中的例子来说明:
我们拿请假的场景来说明,我们请假时间分为3天,5天,7天,对应请假天数审批人不同,可能请假3天由你的项目小组长审批即可,但是请假5天可能需要由部门总来审批,如果是请假7天可能由CTO来审批。在这个场景中就十分适合使用责任链模式进行构建。当然也可以用大量的if-else来处理,但是相对于if-else来讲责任链模式由些好处:

  1. 责任链模式与 if...else 相比,他的耦合性要低一些,因为它将条件判定分散到各个处理类中,并且这些处理类的优先处理顺序可以随意的设定,并且如果想要添加新的 handler 类也是十分简单的,这符合开放闭合原则。
  2. 责任链模式带来了灵活性,但是在设置处理类前后关系时,一定要避免在链中出现循环引用的问题。

如果还不清楚责任链过程可以看看下面的图

责任链1

责任链模式中的角色

  1. Handler:抽象处理者。定义出一个处理请求的接口。接口定义出一个方法以设定和返回对下家的引用。这个角色通常由一个Java抽象类或者Java接口实现。
  2. HandlerImpl:具体处理者。具体处理者接到请求后,可以选择将请求处理掉,或者将请求传给下家。
  3. Client:调用客户端

是不是觉得自己平时写的代码中好像有用到的样子,有点熟悉? 不要急,接下来我们给大家看看一些熟悉的代码,我们上代码。

责任链模式代码示例

  • 我们定义一个抽象处理者用于处理不同的请假天数的。
public abstract class Handler {

    private Handler nextHandler;

    //拿到等级
    protected abstract int getHandlerLevel();

    public final void doHandle(RequestData requestData){
        if(this.getHandlerLevel()==requestData.getLevel()) {
            this.response(requestData);
        }else {
            if(this.nextHandler != null) {
                System.out.println("转到下一个处理者中...");
                this.nextHandler.doHandle(requestData);
            }else {
                System.out.println("后面没有处理请求了");
            }
        }
    }

    /**
     * @param handler 赋值下一个处理节点
     */
    public void setNextHandler(Handler handler) {
        nextHandler = handler;
    }

    //具体操作逻辑
    protected abstract void response(RequestData request);

}
复制代码
  • 定义一个请假天数类,也就是用于判断不同身份处理逻辑的参数类
/**
 * 请假天数定义
 */
public class Level {
    private  int level=0;

    public  Level(int level){
        this.level=level;
    }

    public int getLevel() {
        return level;
    }
}
复制代码
  • 定义一个请求的参数类,里面包含用于判断不同身份处理逻辑的参数类
public class RequestData {
    Level level;
    public  RequestData(Level level){
        this.level=level;
    }
    public int  getLevel(){
        return level.getLevel();
    }

}
复制代码
  • 定义2个身份具体处理者继承抽象处理者
public class ConcreteHandlerB extends Handler {
    @Override
    protected int getHandlerLevel() {
        return 5;
    }

    @Override
    protected void response(RequestData request) {
        System.out.println("请假5天,部门总, 正在处理");
    }

}
===================
  
public class ConcreteHandlerA extends Handler {
    @Override
    protected int getHandlerLevel() {
        return 3;
    }

    @Override
    protected void response(RequestData request) {
        System.out.println("请假3天,小组长,正在处理");
    }

}
复制代码
  • 以上我们定义完,需要进行调用。
//串联成链
Handler handler1=new ConcreteHandlerA();
Handler handler2=new ConcreteHandlerB();
handler1.setNextHandler(handler2);
//进行责任链调用,传递5天
handler1.doHandle(new RequestData(new Level(5)));
复制代码

输出:
转到下一个处理者中...
请假5天,部门总, 正在处理

如果我们传递的是3天
handler1.doHandle(new RequestData(new Level(3)));
输出:
请假3天,小组长,正在处理

责任链模式VS观察者模式

观察者模式我在之前有写过,不了解的可以先看看。责任链模式和观察者模式存在一个共同点,就是传递责任链模式是一级一级的传递,形成一条链,链节点(处理者)之间是存在传递关系的。而观察者模式的被观察者向观察者们的传递,并不是具体观察者之间的传递,观察者之间是不存在关联的。在责任链模式中是请求从小组长到部门总,有层级关系,而对于观察者模式是从被观察者发出,作为观察者的小组长和部门总都会收到对应通知,是扩散式的,二者并没有层级关系。这是责任链模式和观察者模式的区别,也是责任链模式的核心。

责任链模式高级应用

我们了解了上面的基本责任链的使用之后,平时我们常用的组件好多都是用的责任链模式进行实现的。举2个例子比如著名的OKHttp3框架及Netty NIO框架也都使用了责任链模式设计。OkHttp3中Interceptor以及Netty中ChannelInboundHandlerAdapter都是责任链模式设计的典型。那么可以总结为责任链模式是处理数据流比较好的模式设计。我们简单分析一下OKHttp3是如何用于责任链模式的。
责任链2
如上图,框中逻辑就是典型的责任链模式的体现。

责任链模式的优缺点及应用场景

  1. 优点
  • 耦合度降低,请求和处理是分开的
  1. 缺点
  • 责任链太长或者每条链判断处理的时间太长会影响性能。特别是递归循环的时候
  • 不一定被处理,每个职责类的职责很明确,这就需要对写默认的处理了

责任链模式重要的两点:分离职责,动态组合

责任链模式在实际项目中可以用到的地方还是比较多的,比如会员等级系统,会员等级之间构成一条链,用户发起一个请求,系统只要把请求分发到责任链模式的入口,直到传递到与用户会员匹配的等级,这样各个会员等级的业务逻辑就会变成很清晰。