"Design Patterns (4) - Chain of Responsibility Patterns"

"Design Patterns (4) - Chain of Responsibility Patterns"

This article has participated in the "Newcomer Creation Ceremony" activity, and started the road of Nuggets creation together

1. The greater the ability, the greater the responsibility

As the name suggests, a "chain of responsibility" is like a chain of operations connected in series, each of which is closely related. The leave approval process, reimbursement process, etc. are based on the personnel corresponding to different positions to complete the corresponding approval operations; it can be said that it is advanced layer by layer. And the final result we care about is approval or rejection.

2. Chain of Responsibility

When the requesting client sends a request, in order to reduce the coupling between the requester Clientand the processing objects ; at the same time, if many objects are satisfied, they can have the opportunity to participate in the processing of the request, and the objects are formed into a chain. The request is passed along the chain until it is processed and the result is returned.HandlersHandlersHandler

3. Components

By definition, it can be found that the chain of responsibility mainly has several components:

  • Client request class Client: Initiating a request is equivalent to opening the chain of responsibility, submitting tasks to the chain of responsibility and waiting for the processing result.
  • The abstract interface Handleror abstract class of the processing class: contains the definition of the processing request method, and usually also contains the successor chain points.
  • Specific Handlerimplementation class ConcreteHandler: According to the specific implementation of the request according to the size of its own capabilities Client, it is possible that it just matches the call to directly process the returned result chain, otherwise the request is forwarded to the subsequent chain to continue processing until the request processing is completed.
  • The structure diagram is as follows:

Chain of Responsibility Pattern.png

Fourth, simple code implementation
1. Design a file parsing system

The file parsing system matches different parsing objects to parse it according to the imported file format. HandlerBy opening the chain, it is possible to match the appropriate class for the first time, then the call of the chain ends and returns the result; of course, if it is not supported The type of file - then as a program design, can it be simply converted into the current object that cannot process this request, and there is no follow-up Handler, in layman's terms, that is, the request reaches the end of the chain and there is still no suitable one Handlerto process, then the call ends Give the corresponding error message or other operation feedback to the requester Client.

  • 抽象的处理类Handler
/**
 * Created by Sai
 * on: 12/01/2022 23:53.
 * Description:
 */
public abstract class FileParser {
  //后继链
  private FileParser next;

  public void setNext(FileParser next) {
    this.next = next;
  }
  //文件的扩展信息
  protected abstract String getExtension();
  
  protected abstract void doParse(String fileName);

  public void read(String fileName) {
    if (fileName.endsWith(getExtension())) {
      this.doParse(fileName);
      return;
    }

    if (next != null) next.read(fileName);
    else System.out.println("the file type " + fileName + " is unsupported ");
  }
}
复制代码

具体的文件解析实现类、如ExcelMarkDownWord三个组成一条责任链。

  • Excle文件解析类
/**
 * Created by Sai
 * on: 12/01/2022 00:01.
 * Description:
 */
public class ExcelParser extends FileParser {
  @Override
  protected String getExtension() {
    return ".xls";
  }

  @Override
  protected void doParse(String fileName) {
    System.out.println("Parse the excel file...... ");
    System.out.println("-------------------------------------->");
  }
}
复制代码
  • MarkDown文件解析具体实现类
/**
 * Created by Sai
 * on: 12/01/2022 00:03.
 * Description:
 */
public class MarkDownParser extends FileParser {
  @Override
  protected String getExtension() {
    return ".md";
  }

  @Override
  protected void doParse(String fileName) {
    System.out.println("Parse the markdown file......");
    System.out.println("---------------------------------------->");
  }
}
复制代码
  • Word文件解析具体实现类
/**
 * Created by Sai
 * on: 12/01/2022 00:10.
 * Description:
 */
public class WordParser extends FileParser {
    @Override
    protected String getExtension() {
        return ".doc";
    }

    @Override
    protected void doParse(String fileName) {
        System.out.println("Parse the word file......");
        System.out.println("----------------------------------->");
    }
}
复制代码

当然如果确定Client的请求比较明确,像此“文件”解析的系统,只需要导入文件,那么对于链的开启可以单独抽取出来。

  • 实现解析工具类
/**
 * Created by Sai
 * on: 12/01/2022 00:13.
 * Description:
 */
public class FileParserFactory {

  public static FileParser getDataReaderChain() {
    var excelParser = new ExcelParser();
    var markDownParser = new MarkDownParser();
    var wordParser = new WordParser();

    wordParser.setNext(markDownParser);
    markDownParser.setNext(excelParser);
    return wordParser;
  }
}
复制代码
  • Client测试类
/**
 * Created by Sai
 * on: 12/01/2022 00:15.
 * Description:
 */
public class Demo {

    public static void show() {
        var reader = FileParserFactory.getDataReaderChain();
        reader.read("file.xls");
        reader.read("file.md");
        reader.read("file.doc");
        reader.read("file.jpg");
    }

    public static void main(String[] args) {
        show();
    }
}
复制代码
  • 打印信息,显然对于JPG格式系统是不支持,那么也给到了Client相应的反馈信息
Parse the excel file...... 
success...... 
-------------------------------------->
Parse the markdown file......
success...... 
---------------------------------------->
Parse the word file......
success...... 
----------------------------------->
the file type file.jpg is unsupported, failed 

Process finished with exit code 0
复制代码
2.简单的总结

Client作为请求发起者,如导入文件并解析出想要得到的结果。与处理类之间耦合程度低,Client端只管文件的导入,并不会关心最终由谁来处理。即使系统以后需要扩展新的解析类也是非常方便的,只需要继承Handler并单独实现具体细节即可,比较易于扩展的。但是同样也会伴随着系统的膨胀,跟职责的粒度有关。另外观察也可发现,如果责任链太过于长的话,调用栈势必会很深。系统的性能也会打折扣,当然这也是根据具体的业务具体来考虑的。该用的时候想好怎么用,不该用的时候没必要为了设计而设计。毕竟设计模式本质仅仅是为了降低复杂度,降低耦合提高扩展性为目标。

  • Client请求端与处理端Handler耦合度低
  • 职责的分配可以根据业务需求灵活组合,而修改某一具体职责实现细节不影响整体系统的稳定。
  • 易于扩展。

当然缺点也是存在的

  • 当职责的增加,链的长度增加,调用栈深度加深则会影响系统的效率。
  • 职责的具体实现类如果较多,增加了一定的维护成本,同时Client端开启链时复杂度提高。
五、okHttp中的责任链模式

早期Java版本,okHttp3最新的版本为4.9.3,不知道从哪个版本起已经改为Kotlin实现,实现细节作出了调整,但对于拦截器责任链的主要流程变动很小。

  • OkHttpClient相当于我们网络的配置控制中心,包括一些基础的配置,连接池、重试、桥接、协议等,主要配置:Dispatcher(线程调度)设定了最大请求数,单个Host的最大请求数。Protocols支持的协议,HTTP/1.1、HTTP/2。ConnectionSpec对于Socket的设置信息,明确是明文传输的HTTP,还是TLS的HTTPS。 Interceptor,核心链,包含了几个重要的拦截器。当然还包括其他的一些基础参数。
1.newCall

从实际运用的例子入手:

private static void connectNet() {
       OkHttpClient client = new OkHttpClient();
       Request request = new Request.Builder()
               .url("https://www.baidu.com")
               .build();
        //异步请求       
       client.newCall(request).enqueue(new Callback() {
           @Override
           public void onFailure(Call call, IOException e) {
               System.out.println("Failed----->" + e.getMessage());
           }

           @Override
           public void onResponse(Call call, Response response) throws IOException {
               System.out.println("Success----->" + response.toString());
           }
       });
   }
复制代码

newCall(request)方法返回的是一个RealCall对象,实现了Call的接口,当调用RealCall.execute()时:

RealCall.getResponseWithInterceptorChain()会被调用,发起网络请求并拿到返回的响应值Response,同样的异步请求RealCall.enqueue()的调用也是大同小异,主要区别在于Dispatcher的介入,通过线程池的调用将请求加入后台,实际上也是对getResponseWithInterceptorChain()的调用。另外不同的是,对于请求队列的维护是不同的(Dispatcher)中。

getResponseWithInterceptorChain(),计数核心方法,也是OkHttp责任链模式的核心方法,主要的工作就是将多个Interceptor组装起来(List),创建一个RealInterceptorChain对象,而chain.proceed(request)一步步推荐链的执行。

2.Dispatcher
private int maxRequests = 64;
private int maxRequestsPerHost = 5;

private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();

/** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();

/** Running synchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
复制代码
  • 定义了最大异步请求数为64,而单个Host最大的请求数为5,同时对于异步请求,实现了两个双端队列来保存请求runningAsyncCalls、readyAsyncCalls;这个很好理解,当我们的请求已经达到最大值64(或者Host为5),那么此时要是有新的请求过来当然是要将请求先保存起来。对于早期的处理逻辑,当有请求过来时,先判断是否达到请求阀值。决定将请求放入哪个队列当中,在新版本中这个逻辑已经被修改了。同样的对于同步请求是直接入队列runningSyncCalls
//早期的异步请求入队列操作
synchronized void enqueue(AsyncCall call) {
  if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
    runningAsyncCalls.add(call);
    getExecutorService().execute(call);
  } else {
    readyAsyncCalls.add(call);
  }
}
//后期版本中是直接先入到readyAsyncCalls中,当然主要逻辑还是一样的
void enqueue(AsyncCall call) {
  synchronized (this) {
    readyAsyncCalls.add(call);
    if (!call.get().forWebSocket) {
      AsyncCall existingCall = findExistingCallWithHost(call.host());
      if (existingCall != null) call.reuseCallsPerHostFrom(existingCall);
    }
  }
  //在此方法中处理判断是否达到阀值决定是否要加入到runningAsyncCalls。
  promoteAndExecute();
}

private boolean promoteAndExecute() {
    assert (!Thread.holdsLock(this));
    List<AsyncCall> executableCalls = new ArrayList<>();
    boolean isRunning;
    synchronized (this) {
      for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
        AsyncCall asyncCall = i.next();
				//Max capacity.
        if (runningAsyncCalls.size() >= maxRequests) break; 
        //Host max capacity.
        if (asyncCall.callsPerHost().get() >= maxRequestsPerHost) continue; 
        i.remove();
        asyncCall.callsPerHost().incrementAndGet();
        executableCalls.add(asyncCall);
        runningAsyncCalls.add(asyncCall);
      }
      isRunning = runningCallsCount() > 0;
    }

    for (int i = 0, size = executableCalls.size(); i < size; i++) {
      AsyncCall asyncCall = executableCalls.get(i);
      asyncCall.executeOn(executorService());
    }
    return isRunning;
  }
复制代码
  • 同步方法的调用,getResponseWithInterceptorChain(),直接请求返回Response。
@Override 
public Response execute() throws IOException {
  synchronized (this) {
    if (executed) throw new IllegalStateException("Already Executed");
    executed = true;
  }
  transmitter.timeoutEnter();
  transmitter.callStart();
  try {
    client.dispatcher().executed(this);
    return getResponseWithInterceptorChain();
  } finally {
    client.dispatcher().finished(this);
  }
}
复制代码
3.异步请求关键类-AsyncCall
  • 当我们发起异步请求时:
client.newCall(request).enqueue();
@Override 
public void enqueue(Callback responseCallback) {
  synchronized (this) {
    if (executed) throw new IllegalStateException("Already Executed");
    executed = true;
  }
  transmitter.callStart();
  //AsyncCall继承了抽象类NamedRunnable(实现了Runnable接口),其实就是线程的实现,对于run()方法中的具体逻辑,增加了抽象方法execute(),看看具体实现。
  client.dispatcher().enqueue(new AsyncCall(responseCallback));
}

	//AsyncCall#execute()
	@Override 
  protected void execute() {
  boolean signalledCallback = false;
  transmitter.timeoutEnter();
  try {
    //无论异步请求还是同步请求,本质都是对getResponseWithInterceptorChain()调用,只是异步请求增加了线程的管理与调度。
    Response response = getResponseWithInterceptorChain();
    signalledCallback = true;
    responseCallback.onResponse(RealCall.this, response);
  } catch (IOException e) {
    if (signalledCallback) {
      //Do not signal the callback twice!
      Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
    } else {
      responseCallback.onFailure(RealCall.this, e);
    }
  } catch (Throwable t) {
    cancel();
    if (!signalledCallback) {
      IOException canceledException = new IOException("canceled due to " + t);
      canceledException.addSuppressed(t);
      responseCallback.onFailure(RealCall.this, canceledException);
    }
    throw t;
  } finally {
    client.dispatcher().finished(this);
  }
}
复制代码
4.关键方法-getResponseWithInterceptorChain()
  • RealCall#getResponseWithInterceptorChain()
Response getResponseWithInterceptorChain() throws IOException {
  //Build a full stack of interceptors.
  //拦截器集合
  List<Interceptor> interceptors = new ArrayList<>();
  //用户可以自定拦截器,这里保存了用户自定义的拦截器
  interceptors.addAll(client.interceptors());
  //重试拦截器
  interceptors.add(new RetryAndFollowUpInterceptor(client));
  //桥接拦截器,包括gzip的压缩,host信息的设置等
  interceptors.add(new BridgeInterceptor(client.cookieJar()));
  //缓存请求
  interceptors.add(new CacheInterceptor(client.internalCache()));
  //这个拦截器的代码量很少,主要就是与服务器建立链接TCP链接或TCP-TLS链接
  interceptors.add(new ConnectInterceptor(client));
  if (!forWebSocket) {
    interceptors.addAll(client.networkInterceptors());
  }
  //责任链尾,实质上的请求与I/O操作,将请求数据写入Socket中,并从Socket读取响应数据(TCP/TCP-TLS对应的端口)。
  interceptors.add(new CallServerInterceptor(forWebSocket));
  Interceptor.Chain chain = new RealInterceptorChain(interceptors, transmitter, null, 0,
      originalRequest, this, client.connectTimeoutMillis(),
      client.readTimeoutMillis(), client.writeTimeoutMillis());
  boolean calledNoMoreExchanges = false;
  try {
    Response response = chain.proceed(originalRequest);
    if (transmitter.isCanceled()) {
      closeQuietly(response);
      throw new IOException("Canceled");
    }
    return response;
  } catch (IOException e) {
    calledNoMoreExchanges = true;
    throw transmitter.noMoreExchanges(e);
  } finally {
    if (!calledNoMoreExchanges) {
      transmitter.noMoreExchanges(null);
    }
  }
}
复制代码
  • 我们的Response经过核心方法getResponseWithInterceptorChain()的包装,最终拿到了想要的结果,这里是OkHttp责任链模式的核心,设计的很巧妙。看看具体做了哪些操作。
5.RetryAndFollowUpInterceptor
  • 拦截器都实现了统一的接口Interceptor,看其中的关键方法:
//定义了默认最大重试次数20次
private static final int MAX_FOLLOW_UPS = 20;
@Override 
public Response intercept(Chain chain) throws IOException {
  Request request = chain.request();
  RealInterceptorChain realChain = (RealInterceptorChain) chain;
  Transmitter transmitter = realChain.transmitter();
  int followUpCount = 0;
  Response priorResponse = null;
  while (true) {
    transmitter.prepareToConnect(request);
    if (transmitter.isCanceled()) {
      throw new IOException("Canceled");
    }
    Response response;
    boolean success = false;
    try {
      //分界点,包括其他的拦截器,在责任链传递之前所做的工作都是前序工作,之后将request继续下发
      response = realChain.proceed(request, transmitter, null);
      //此后便是拦截器的后序工作,需要注意的是,并不是每次都会走完所有的拦截器,如cacheInterceptor,当有缓存存在(开启缓存),那么之后的拦截就不在继续传递(分情况,会有一个比对的过程)。
      success = true;
    } catch (RouteException e) {
      //The attempt to connect via a route failed. The request will not have been sent.
      if (!recover(e.getLastConnectException(), transmitter, false, request)) {
        throw e.getFirstConnectException();
      }
      continue;
    } catch (IOException e) {
      // An attempt to communicate with a server failed. The request may have been sent.
      boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
      if (!recover(e, transmitter, requestSendStarted, request)) throw e;
      continue;
    } finally {
      // The network call threw an exception. Release any resources.
      if (!success) {
        transmitter.exchangeDoneDueToException();
      }
    }
    // Attach the prior response if it exists. Such responses never have a body.
    if (priorResponse != null) {
      response = response.newBuilder()
          .priorResponse(priorResponse.newBuilder()
                  .body(null)
                  .build())
          .build();
    }
    Exchange exchange = Internal.instance.exchange(response);
    Route route = exchange != null ? exchange.connection().route() : null;
    Request followUp = followUpRequest(response, route);
    if (followUp == null) {
      if (exchange != null && exchange.isDuplex()) {
        transmitter.timeoutEarlyExit();
      }
      return response;
    }
    RequestBody followUpBody = followUp.body();
    if (followUpBody != null && followUpBody.isOneShot()) {
      return response;
    }
    closeQuietly(response.body());
    if (transmitter.hasExchange()) {
      exchange.detachWithViolence();
    }
    if (++followUpCount > MAX_FOLLOW_UPS) {
      throw new ProtocolException("Too many follow-up requests: " + followUpCount);
    }
    request = followUp;
    priorResponse = response;
  }
}
复制代码
  • RealInterceptorChain关键方法proceed,之前已经了解,在RealCall是整个链开始传递的起点:
//RealCall,可以看到index为0,我们所有的拦截都被保存在list集合之中,可以发现后序的取interceptor都是基于这个index自增来获取。
//这也是精妙之处
Interceptor.Chain chain = new RealInterceptorChain(interceptors, transmitter, null, 0,
       originalRequest, this, client.connectTimeoutMillis(),
       client.readTimeoutMillis(), client.writeTimeoutMillis());
//RealInterceptorChain#proceed()
public Response proceed(Request request, Transmitter transmitter, @Nullable Exchange exchange)
     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.exchange != null && !this.exchange.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.exchange != null && calls > 1) {
     throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
         + " must call proceed() exactly once");
   }
   //Call the next interceptor in the chain.
   //这里对index做了自增操作,因为每次实例化RealInterceptorChain,传入的都是初始的interceptor集合,当每次调用proceed时都对index操作,这样
   //我们的request就被一步步传递下去直到链尾。
   RealInterceptorChain next = new RealInterceptorChain(interceptors, transmitter, exchange,
       index + 1, request, call, 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 (exchange != 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;
 }
复制代码
6.ConnectInterceptor
  • 主要就是与服务器建立链接TCP链接或TCP-TLS链接,这个比较特殊,之前提到拦截的前序操作基于调用方法realChain.proceed()之前,但是这个是没有后序操作的:
/** Opens a connection to the target server and proceeds to the next interceptor. */
//代码量很少,建立链接
public final class ConnectInterceptor implements Interceptor {
  public final OkHttpClient client;
  public ConnectInterceptor(OkHttpClient client) {
    this.client = client;
  }
  @Override 
  public Response intercept(Chain chain) throws IOException {
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    Request request = realChain.request();
    Transmitter transmitter = realChain.transmitter();
    //We need the network to satisfy this request. Possibly for validating a conditional GET.
    boolean doExtensiveHealthChecks = !request.method().equals("GET");
    Exchange exchange = transmitter.newExchange(chain, doExtensiveHealthChecks);
    //下发传递
    return realChain.proceed(request, transmitter, exchange);
  }
}
复制代码
7.CallServerInterceptor
/** This is the last interceptor in the chain. It makes a network call to the server. */
public final class CallServerInterceptor implements Interceptor {
  private final boolean forWebSocket;
  public CallServerInterceptor(boolean forWebSocket) {
    this.forWebSocket = forWebSocket;
  }

  @Override
  public Response intercept(Chain chain) throws IOException {
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    Exchange exchange = realChain.exchange();
    Request request = realChain.request();
    long sentRequestMillis = System.currentTimeMillis();
    exchange.writeRequestHeaders(request);
    boolean responseHeadersStarted = false;
    Response.Builder responseBuilder = null;
    if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
      // If there's a "Expect: 100-continue" header on the request, wait for a "HTTP/1.1 100
      // Continue" response before transmitting the request body. If we don't get that, return
      // what we did get (such as a 4xx response) without ever transmitting the request body.
      if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
        exchange.flushRequest();
        responseHeadersStarted = true;
        exchange.responseHeadersStart();
        responseBuilder = exchange.readResponseHeaders(true);
      }
      if (responseBuilder == null) {
        if (request.body().isDuplex()) {
          // Prepare a duplex body so that the application can send a request body later.
          exchange.flushRequest();
          BufferedSink bufferedRequestBody = Okio.buffer(
              exchange.createRequestBody(request, true));
          request.body().writeTo(bufferedRequestBody);
        } else {
          // Write the request body if the "Expect: 100-continue" expectation was met.
          BufferedSink bufferedRequestBody = Okio.buffer(
              exchange.createRequestBody(request, false));
          request.body().writeTo(bufferedRequestBody);
          bufferedRequestBody.close();
        }
      } else {
        exchange.noRequestBody();
        if (!exchange.connection().isMultiplexed()) {
          // If the "Expect: 100-continue" expectation wasn't met, prevent the HTTP/1 connection
          // from being reused. Otherwise we're still obligated to transmit the request body to
          // leave the connection in a consistent state.
          exchange.noNewExchangesOnConnection();
        }
      }
    } else {
      exchange.noRequestBody();
    }
    if (request.body() == null || !request.body().isDuplex()) {
      exchange.finishRequest();
    }
    if (!responseHeadersStarted) {
      exchange.responseHeadersStart();
    }
    if (responseBuilder == null) {
      responseBuilder = exchange.readResponseHeaders(false);
    }
    Response response = responseBuilder
        .request(request)
        .handshake(exchange.connection().handshake())
        .sentRequestAtMillis(sentRequestMillis)
        .receivedResponseAtMillis(System.currentTimeMillis())
        .build();
    int code = response.code();
    if (code == 100) {
      // server sent a 100-continue even though we did not request one.
      // try again to read the actual response
      response = exchange.readResponseHeaders(false)
          .request(request)
          .handshake(exchange.connection().handshake())
          .sentRequestAtMillis(sentRequestMillis)
          .receivedResponseAtMillis(System.currentTimeMillis())
          .build();
      code = response.code();
    }
    exchange.responseHeadersEnd(response);
    if (forWebSocket && code == 101) {
      //Connection is upgrading, but we need to ensure interceptors see a non-null response body.
      response = response.newBuilder()
          .body(Util.EMPTY_RESPONSE)
          .build();
    } else {
      response = response.newBuilder()
          .body(exchange.openResponseBody(response))
          .build();
    }
    if ("close".equalsIgnoreCase(response.request().header("Connection"))
        || "close".equalsIgnoreCase(response.header("Connection"))) {
      exchange.noNewExchangesOnConnection();
    }
    if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
      throw new ProtocolException(
          "HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
    }
    return response;
  }
}
复制代码
  • At the end of the chain of responsibility, the actual request and I/O operations, write the request data into the Socket, and read the response data (the port corresponding to TCP/TCP-TLS) from the Socket. Efficient requests for I/Owhich operations are based Okioare also inseparable from support. The entire flowchart is:OkHttpOkiotp451s.png
6. What problems can be solved?
  • In summary, it can be found that the same request can be processed by a series of objects in combination, but which object is processed by the dynamic decision (runtime), then the objects can be combined to form a chain, and consider whether the chain of responsibility can be simplified to make Clear structure.
  • For the case where the receiver is not clear, but multiple objects can handle the same request, in order to reduce Clientthe Handlercoupling between them, consider using the chain of responsibility.

Guess you like

Origin juejin.im/post/7052512275021856781