Java实现责任链模式

2年前 (2022) 程序员胖胖胖虎阿
252 0 0

什么是责任链设计模式

责任链设计模式是软件开发中常见的一种设计模式,在该模式中将一个请求进行递进处理,从而形成一条链,在链条的节点中处理请求,一个节点处理完自己的逻辑再传递到下一个节点中。

在许多开源库中,可以看到责任链模式的使用,例如OkHttp、PermissionX、Spring中的Filter等。

在日常开发中,例如登录校验、返回值处理、多个弹框都可以使用责任链模式,下面使用两个案例来实现责任链模式。

日常案例

需求描述:使用责任链模式实现员工假期审批流程,组长可以审批小于3天的假期,经理可以审批小于10天的假期,老板可以审批10天以上的假期。

分析:使用面向对象的方式,在需求中假期是需要处理的业务,组长、经理和老板属于责任链中的处理节点。

每个节点处理的时候如果满足要求则返回处理结果,如果不满足要求则转到下一个节点处理。

一、创建假期类Event

public class Event {

    // 请假天数
    private int date;


    public int getDate() {
        return date;
    }

    public void setDate(int date) {
        this.date = date;
    }

}

二、创建责任链

1、创建链节点抽象类

每个处理节点对象需要继承该抽象类,并且实现proceed方法,在proceed方法中实现执行逻辑,如果满足要求则执行返回,如果不满足要求则交给下一个链节点。

使用构造者模式来添加链节点,参考文章:这才是责任链模式的优雅使用方式

public abstract class CheckChain {

    // 当前处理节点
    protected CheckChain checker;

    // 设置下一个处理者
    public void setNextChecker(CheckChain checker) {
        this.checker = checker;
    }

    // 处理方法,每一个处理者要实现该方法
    public abstract void proceed(Event event);

    // 使用构造者模式创建
    public static class Builder {
        // 分别记录第一个处理者和下一个处理者,类似于链表结构
        private CheckChain head;
        private CheckChain tail;

        // 添加处理者
        public Builder addChecker(CheckChain chain) {
            if (this.head == null) {
                this.head = this.tail = chain;
                return this;
            }
            // 设置下一个处理者
            this.tail.setNextChecker(chain);
            this.tail = chain;
            return this;
        }

        public CheckChain build() {
            return this.head;
        }
    }
}

2、创建责任链节点

分别创建组长、经理、老板处理节点。

组长链节点,组长可以审批小于3天假,超过3天的需要到经理审批。

public class GroupChain extends CheckChain {

     @Override
    public boolean proceed(Event event) {
        if (event.getDate() > 3) {
            System.out.println("组长权限不够,转到上级领导审批");
            if (checker != null) {
                return checker.proceed(event);
            }
            return false;
        } else {
            System.out.println("组长审批通过");
            return true;
        }
    }
}

经理链节点,经理可以审批小于10天假,超过10天的需要到老板审批。

public class ManagerChain extends CheckChain {

     @Override
    public boolean proceed(Event event) {
        if (event.getDate() > 10) {
            System.out.println("经理权限不够,转到上级领导审批");
            if (checker != null) {
                return checker.proceed(event);
            }
            return false;
        } else {
            System.out.println("经理审批通过");
            return true;
        }
    }
}

老板可以审批所有假期。

public class BossChain extends CheckChain {

     @Override
    public boolean proceed(Event event) {
        System.out.println("老板审批通过");
        return true;
    }
}

三、测试程序

// 创建请假条,请假30天
Event event = new Event();
event.setDate(30);
// 创建组长、经理、老板链节点
GroupChain groupChain = new GroupChain();
new CheckChain.Builder()
        .addChecker(groupChain)
        .addChecker(new ManagerChain())
        .addChecker(new BossChain())
        .build();
// 组长发起开始执行操作
boolean result = groupChain.proceed(event);
System.out.println("请假是否成功:" + result);

执行结果:

组长权限不够,转到上级领导审批
经理权限不够,转到上级领导审批
老板审批通过
请假是否成功:true

该测试程序中对于请假人来说,不关注谁有权限审批假期,而关注的是最终是否请假成功。所以责任链模式是只关注结果,不关注过程。

手写OkHttp责任链

上面的实现只是责任链模式实现的一种,在OkHttp中责任链的实现略有不同,为了更深层次熟悉责任链模式,下面通过手写OkHttp中责任链的实现方式,来进一步了解责任链模式和OkHttp原理。

首先看一段发起网络请求的代码:

// 通过构造者模式来初始化OkHttpClient
OkClient client = new OkClient.Builder()
        .addInterceptor(new BridgeInterceptor())
        .addInterceptor(new NetworkInterceptor())
        .addInterceptor(new LogInterceptor())
        .addInterceptor(new CallServerInterceptor())
        .build();
// 通过构造者模式来创建请求
Request request = new Request.Builder()
        .url("http:123.com")
        .build();
// 发起请求后得到执行结果
Response response = client.newCall(request).execute();
// 输出执行结果
System.out.println(response.toString());

上面的代码是仿照OkHttp来实现的一个网络请求,下面来看一下这个请求过程是如何通过责任链模式实现的。

一、创建OkClient类,该类通过构造者模式来添加相应的参数

public class OkClient {

    // 创建一个集合来存储拦截器
    private ArrayList<Interceptor> interceptors = new ArrayList<>();

    public OkClient(Builder builder) {
        this.interceptors = builder.arrayList;
    }

    // 返回拦截器集合
    public List<Interceptor> interceptors() {
        return interceptors;
    }

    // 创建发起请求方法
    public RealCall newCall(Request request) {
        return new RealCall(this, request);
    }

    public static class Builder {

        private ArrayList<Interceptor> arrayList = new ArrayList<>();

        public Builder addInterceptor(Interceptor interceptor) {
            arrayList.add(interceptor);
            return this;
        }

        public OkClient build() {
            return new OkClient(this);
        }
    }
}

二、创建责任链接口

OkHttp中是使用接口,上一个案例是使用抽象类。

1、Interceptor接口和内部Chain接口

public interface Interceptor {

    // 拦截处理响应
    Response intercept(Chain chain);

    // OkHttp责任链中不仅处理Request请求也处理Response请求
    interface Chain {

        Request request();

        Response proceed(Request request);
    }
}

三、创建Request类和Response类

1、Request类
Request类时通过构造者模式创建,如下所示,Request类会传递请求的url、请求类型、请求头和请求体,会在拦截器中校验和设置这些值。

public class Request {

    private String url;
    private String mediaType;
    private String body;
    private String header;

    public Request(Builder builder) {
        this.url = builder.url;
    }

    public String getBody() {
        return body;
    }

    public void setBody(String body) {
        this.body = body;
    }

    public String getHeader() {
        return header;
    }

    public void setHeader(String header) {
        this.header = header;
    }

    public String getMediaType() {
        return mediaType;
    }

    public void setMediaType(String mediaType) {
        this.mediaType = mediaType;
    }

    public static class Builder {

        private String url;

        public Builder url(String url) {
            this.url = url;
            return this;
        }

        public Request build() {
            return new Request(this);
        }
    }
}

2、Response类
Response类比较简单,返回所需要的打印信息,如下所示:

public class Response {

    private String bridgeInterceptor;
    private String logInterceptor;
    private String networkInterceptor;
    private String callServerInterceptor;
    // 省略部分代码
}

四、创建拦截器

创建的拦截器需要实现Interceptor接口,分别创建BridgeInterceptor、NetworkInterceptor、LogInterceptor、CallServerInterceptor。

1、创建BridgeInterceptor类

public class BridgeInterceptor implements Interceptor {

    @Override
    public Response intercept(Chain chain) {
        // 处理请求
        Request request = chain.request();
        String mediaType = request.getMediaType();
        // 在该拦截器中设置MediaType
        if (TextUtils.isEmpty(mediaType)) {
            request.setMediaType("BridgeInterceptor");
        }
        // 处理响应结果
        Response response = chain.proceed(request);
        // 给响应结果设置值
        if (TextUtils.isEmpty(response.getBridgeInterceptor())) {
            response.setBridgeInterceptor("BridgeInterceptor");
        }
        return response;
    }
}

2、创建NetworkInterceptor类

public class NetworkInterceptor implements Interceptor {

    @Override
    public Response intercept(Chain chain) {
        // 处理请求
        Request request = chain.request();
        // 在该拦截器中设置请求头
        if (TextUtils.isEmpty(request.getHeader())) {
            request.setHeader("RetryInterceptor");
        }
        // 处理响应结果
        Response response = chain.proceed(request);
        // 给响应结果设置值
        if (TextUtils.isEmpty(response.getNetworkInterceptor())) {
            response.setNetworkInterceptor("NetworkInterceptor");
        }
        return response;
    }
}

3、创建LogInterceptor类

public class LogInterceptor implements Interceptor {

    @Override
    public Response intercept(Chain chain) {
        Request request = chain.request();
        // 在该拦截器中设置请求体
        if (TextUtils.isEmpty(request.getBody())) {
            request.setBody("LogInterceptor");
        }
        // 处理响应结果
        Response response = chain.proceed(request);
        // 给响应结果设置值
        if (TextUtils.isEmpty(response.getLogInterceptor())) {
            response.setLogInterceptor("LogInterceptor");
        }
        return response;
    }
}

4、创建CallServerInterceptor类

CallServerInterceptor拦截器属于OkHttp中最后一个拦截器,不会再执行proceed方法,会把Response直接返回

public class CallServerInterceptor implements Interceptor {

    @Override
    public Response intercept(Chain chain) {
        Request request = chain.request();
        System.out.println("CallServerInterceptor: " + request.toString());
        // 如果请求头,请求类型,请求体都不为空,则发起网络请求,返回结果
        if (!TextUtils.isEmpty(request.getHeader()) && !TextUtils.isEmpty(request.getMediaType())
                && !TextUtils.isEmpty(request.getBody())) {
            // 发起网络请求,返回结果
            Response response = new Response();
            response.setCallServerInterceptor("CallServerInterceptor");
            return response;
        }
        return null;
    }
}

五、创建RealCall类

在OkHttp中,调用流程是OkHttpClient.newCall() --> 创建RealCall–>执行RealCall.execute()方法 --> RealCall.getResponseWithInterceptorChain()方法,在该方法中添加拦截器并调用第一个拦截器的proceed方法;

public Response getResponseWithInterceptorChain() {
    List<Interceptor> interceptors = new ArrayList<>();
    interceptors.addAll(client.interceptors());
    Interceptor.Chain chain = new RealInterceptorChain(interceptors, 0, this.request);
    return chain.proceed(request);
}

六、创建RealInterceptorChain类

RealInterceptorChain类中的proceed方法内通过index索引开始执行责任链的调用。

public class RealInterceptorChain implements Interceptor.Chain {

    private int index;
    private List<Interceptor> interceptors;
    private Request request;

    public RealInterceptorChain(List<Interceptor> interceptors, int index, Request request) {
        this.interceptors = interceptors;
        this.index = index;
        this.request = request;
    }

    @Override
    public Request request() {
        return this.request;
    }

    @Override
    public Response proceed(Request request) {
        if (index >= interceptors.size()) throw new Error("当前索引和拦截器长度不配");
        // 得到下一个责任链拦截器,将index+1
        RealInterceptorChain next = new RealInterceptorChain(interceptors, index + 1, request);
        // 获取当前拦截器
        Interceptor interceptor = interceptors.get(index);
        // 执行拦截方法,并把下一个拦截器传递
        Response response = interceptor.intercept(next);
        return response;
    }
}

七、最终输出结果

// 打印出在请求过程中添加的请求参数
CallServerInterceptor: Request{url='http:123.com', mediaType='BridgeInterceptor', body='LogInterceptor', header='RetryInterceptor'}
// 返回的Response中有了4个拦截器结果
Response{bridgeInterceptor='BridgeInterceptor', logInterceptor='LogInterceptor', networkInterceptor='NetworkInterceptor', callServerInterceptor='CallServerInterceptor'}

整个OkHttp中责任链模式已经实现,可见OkHttp中的实现也是很优雅的。通过本文不仅可以学习责任链模式原理,还能熟悉OkHttp的请求和返回过程中拦截器的作用原理。

上面源码可以查看工程034-android-chain

个人公众号,喜欢的可以关注一下:
Java实现责任链模式

版权声明:程序员胖胖胖虎阿 发表于 2022年10月27日 上午1:24。
转载请注明:Java实现责任链模式 | 胖虎的工具箱-编程导航

相关文章

暂无评论

暂无评论...