责任链模式
建造者模式(Chain of Responsibility):有多个对象都有机会处理某个请求,从第一个请求开始,都会持有一个引用指向下一个请求(最后一个请求指向null
),从而形成一条链,沿着这条链传递请求,直到请求被处理或者传递到最后一个请求结束。
通用类图
责任链模式的通用类图如下所示:
优缺点
责任链模式的主要优点
- 责任链模式的显著优点是将请求和处理分离,两者各自注重实现自己的功能,符合类的单一职责。
- 功能修改和新增比较简单,新建链条替换原有即可
- 配合其他模式的时候扩展性和可编程性非常好,比如下文即将提到的
OkHttp
中的intercepter
的应用,就是用了模板方法模式+责任链模式。
责任链模式的主要缺点
- 责任链模式的显著缺点是性能问题,一个请求必须从头遍历整个链条,直到找到符合要求的处理类,在链条特别长,性能是个很大的问题。
OkHttp中的责任链模式
在OkHttp中OkHttpClient
中就有很明显的责任链模式的使用intercepter
,我们先看看OkHttp中责任链模式的大致类图。
可以明显看出和一般的责任链类图略有不同,属于变形的责任链,下面我们通过源码来看OkHttpClient
中Interceptor
中的责任链是怎么使用的。Interceptor
是网络请求的返回结果的拦截器,所以处理逻辑发生在处理http请求之后,http处理请求是发生在RealCall
中excute()
处理的,下面看下RealCall
中的源码,
@Override
public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
timeout.enter();
eventListener.callStart(this);
try {
client.dispatcher().executed(this);
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} catch (IOException e) {
e = timeoutExit(e);
eventListener.callFailed(this, e);
throw e;
} finally {
client.dispatcher().finished(this);
}
}
......
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors());
interceptors.add(retryAndFollowUpInterceptor);
interceptors.add(new BridgeInterceptor(client.cookieJar()));
interceptors.add(new CacheInterceptor(client.internalCache()));
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
interceptors.addAll(client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(forWebSocket));
Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
return chain.proceed(originalRequest);
}
注意看execute()
方法中try
代码块调用了getResponseWithInterceptorChain()
方法,就是处理用户定义以及框架定义的Interceptor
逻辑。先新建一个集合,然后将用户配置的Interceptor
集合放进去,再放入框架默认的Interceptor
形成一个新集合。接下来就是重点,将Interceptor
集合,请求对象(注意参数index初始值为0)等参数封装到RealInterceptorChain
对象中,然后调用该对象的proceed()
方法启动整个责任链执行工作。RealInterceptorChain
对象的proceed()
会将下一个Interceptor
封装成新的RealInterceptorChain
对象next
,并将它通过当前Interceptor
的对象的interceptor.intercept(next)
方法连接到当前处理的下一个RealInterceptorChain
,简单的调用链接图如下:
@Override public Response proceed(Request request) throws IOException {
return proceed(request, streamAllocation, httpCodec, connection);
}
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection) throws IOException {
if (index >= interceptors.size()) throw new AssertionError();
calls++;
// Call the next interceptor in the chain.
RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
writeTimeout);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);
return response;
}
我对代码做了些简化,去掉了一些参数检查之类的,可以看到最后执行到RealinterceptorChain
中的重载方法proceed()
中,可以看到通过不断生成下一个RealinterceptorChain
对象,并将新的RealinterceptorChain
对象传递给下一个Interceptor
,形成处理链条,形成责任链模式。此外这里还用到了模板方法模式,所谓模板方法模式,就是定义了一些固定的算法(执行步骤),然后提供某些待实现的抽象方法或者接口方法给其他子类或接口对象做扩展功能使用。开发中常常会遇到模板方法模式,比如Activity
、Fragment
的基类等。
OkHttp中Interceptor的使用方式
我们来看看OkHttp中Interceptor是怎么样使用的,下面代码就是一个简单的使用:
OkHttpClient httpClient = new OkHttpClient.Builder()
.addInterceptor(new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request()
.newBuilder()
.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
.addHeader("Connection", "keep-alive")
.addHeader("Accept", "*/*")
.addHeader("platform", "android")//平台
.addHeader("version", SSystem.getVersionName(SApplication.getAppContext()))//版本
.build();
return chain.proceed(request);
}
}).addInterceptor(new LogInterceptor())
.addInterceptor(new HttpLoggingInterceptor(logger).setLevel(HttpLoggingInterceptor.Level.BODY))
.connectTimeout(10, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.cache(cache)
.build();
由上面的代码可以看出,可以直接通过Interceptor
接口new
一个匿名类对象,实现接口方法,也可以new
一个实现了Interceptor
接口的类,然后add
进去。通过上面的源码分析我们发现责任链顺序是以我们add
的顺序是一致的,而且用户自定义的排在前面,所以我们可以通过add
顺序实现责任链的处理顺序。
OkHttp这样使用责任链的好处
先来思考一下,如果不使用责任链模式来处理,结果会怎样?是不是会用大量的if--esle
来判断要处理的逻辑?如果有新需求或删除原有需求,是不是还要去大量的if--else
代码中找,并且修改?严重违反了Java开发的开闭原则,也不符合类的单一原则。所以OkHttp中这样使用责任链的好处是:
- 符合开闭原则,拥抱改变,扩展性性更强
- 符合类的单一原则,使类结构更加清晰
- 用户的可配置性强,可自由定制个性化需求,这对于一个开源框架来说是非常重要的