浅谈责任链模式
2019-08-21 本文已影响0人
程序员阿兵
责任链模式(Chain of Responsibility Pattern)
使多个对象都有处理请求的机会,从而避免了请求的发送者和接收者之间的耦合关系。将这些对象串成一条链,并沿着这条链一直传递该请求,直到有对象处理它为止。
- 责任链模式的重点在“链上”,由一条链去处理相似的请求,在链中决定谁来处理这个请求,并返回相应的结果。
演示代码
- 新建几个任务,每个任务执行不同的需要 分别为 task1 task2 ...
public class Task1 extends BaseTask {
public Task1(boolean isTask) {
super(isTask);
}
@Override
public void doAction() {
// // 执行 子节点 链条断
System.out.println("Task1 任务节点一 执行了");
}
}
- 每个任务继承父类
public abstract class BaseTask {
// 判断当前任务节点 有没有能力执行 有
private boolean isTask;
public BaseTask(boolean isTask) {
this.isTask = isTask;
}
// 执行下一个节点
private BaseTask nextTask; // t2,t3,t4
// 添加下一个节点任务
public void addNextTask(BaseTask nextTask) {
this.nextTask = nextTask;
}
// 让子节点任务去完成的
public abstract void doAction();
public void action() { // t1=false,t2=false,t3=true,
if (isTask) { // t3
doAction(); // 执行 子节点 链条断
} else {
// 继续执行下一个 任务节点
if (nextTask != null) nextTask.action();
}
}
}
- 执行所以的任务
Task1 task1 = new Task1(false);
Task2 task2 = new Task2(false);
Task3 task3 = new Task3(true);
Task4 task4 = new Task4(false);
task1.addNextTask(task2);
task2.addNextTask(task3);
task3.addNextTask(task4);
// 执行第一个任务节点
task1.action();
由上面的代码可以,在task1 执行action时,addNextTask会传人下个任务,只有在当前的任务满足条件时会处理任务。
在翻阅okhttp的源码时,会发现在返回response时就是用到责任链模式:
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);
}
将所有的拦截器缓存加入到RealInterceptorChain中,下面可以看到 RealInterceptorChain父类proceed方法:
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection) throws IOException {
if (index >= interceptors.size()) throw new AssertionError();
calls++;
// If we already have a stream, confirm that the incoming request will use it.
if (this.httpCodec != null && !this.connection.supportsUrl(request.url())) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must retain the same host and port");
}
// If we already have a stream, confirm that this is the only call to chain.proceed().
if (this.httpCodec != null && calls > 1) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must call proceed() exactly once");
}
// 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);
// Confirm that the next interceptor made its required call to chain.proceed().
if (httpCodec != null && index + 1 < interceptors.size() && next.calls != 1) {
throw new IllegalStateException("network interceptor " + interceptor
+ " must call proceed() exactly once");
}
// Confirm that the intercepted response isn't null.
if (response == null) {
throw new NullPointerException("interceptor " + interceptor + " returned null");
}
if (response.body() == null) {
throw new IllegalStateException(
"interceptor " + interceptor + " returned a response with no body");
}
return response;
}
}
在上面方法中会发现又重新new 了RealInterceptorChain不同的是 传人的index由第一次0改变为index++模式,每次取出数组中不同的拦截器去执行,当拦截器处理完并且不需要走向下一步,可以直接返回最终response!
// 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);
根据okhttp的责任链模式,可以对应去优化自己实现:
- 第一步依然是第一父类任务:
public interface IBaseTask {
/**
* 参数一:任务节点是否有能力执行
* 参数二:下一个任务节点
* @param isTask
*/
public void doRunAction(String isTask, IBaseTask iBaseTask);
}
- 不同任务 task1 2 3....
public class Task1 implements IBaseTask {
@Override
public void doRunAction(String isTask, IBaseTask iBaseTask) { // iBaseTask == ChainManager
if ("no".equals(isTask)) {
System.out.println("拦截器 任务节点一 处理了...");
return;
} else {
// 继续执行下一个链条的任务节点 ChainManager.doRunAction("ok", ChainManager)
// ChainManager.doRunAction
iBaseTask.doRunAction(isTask, iBaseTask);
}
}
}
- 根据 okhttp RealInterceptorChain 的思想,可以将所有的任务缓存起来
public class ChainManager implements IBaseTask {
private List<IBaseTask> iBaseTaskList = new ArrayList<>();
public void addTask(IBaseTask iBaseTask) {
iBaseTaskList.add(iBaseTask);
}
private int index = 0;
@Override
public void doRunAction(String isTask, IBaseTask iBaseTask) {
if (iBaseTaskList.isEmpty()) {
// 抛出异常..
return;
}
if (index == iBaseTaskList.size() || index > iBaseTaskList.size()) {
return;
}
IBaseTask iBaseTaskResult = iBaseTaskList.get(index); // index 0 t1, index 1 t2 index 2 t3
index ++;
// iBaseTaskResult本质==Task1, iBaseTaskResult本质==Task2 iBaseTaskResult本质==Task3
iBaseTaskResult.doRunAction(isTask, iBaseTask);
}
}
- 调用
public class Test {
public static void main(String[] args) {
ChainManager chainManager = new ChainManager();
chainManager.addTask(new Task1());
chainManager.addTask(new Task2());
chainManager.addTask(new Task3());
chainManager.doRunAction("ok", chainManager);
}
}
这样就和okhttp责任链模式神似了,唯一区别是我将chainManager也就是RealInterceptorChain 对象之前传传入调用当前任务,在内部改变index,而okhttp的思想是每次执行任务会重新new RealInterceptorChain(index),效果都是一样。