從OkHttp中學習設計模式---責任鏈模式

責任鏈模式

建造者模式(Chain of Responsibility):有多個對象都有機會處理某個請求,從第一個請求開始,都會持有一個引用指向下一個請求(最後一個請求指向null),從而形成一條鏈,沿着這條鏈傳遞請求,直到請求被處理或者傳遞到最後一個請求結束。

通用類圖

責任鏈模式的通用類圖如下所示:
責任鏈模式類圖

優缺點

責任鏈模式的主要優點

  • 責任鏈模式的顯著優點是將請求和處理分離,兩者各自注重實現自己的功能,符合類的單一職責。
  • 功能修改和新增比較簡單,新建鏈條替換原有即可
  • 配合其他模式的時候擴展性和可編程性非常好,比如下文即將提到的OkHttp中的intercepter的應用,就是用了模板方法模式+責任鏈模式。

責任鏈模式的主要缺點

  • 責任鏈模式的顯著缺點是性能問題,一個請求必須從頭遍歷整個鏈條,直到找到符合要求的處理類,在鏈條特別長,性能是個很大的問題。

OkHttp中的責任鏈模式

在OkHttp中OkHttpClient中就有很明顯的責任鏈模式的使用intercepter,我們先看看OkHttp中責任鏈模式的大致類圖。
在這裏插入圖片描述
可以明顯看出和一般的責任鏈類圖略有不同,屬於變形的責任鏈,下面我們通過源碼來看OkHttpClientInterceptor中的責任鏈是怎麼使用的。Interceptor是網絡請求的返回結果的攔截器,所以處理邏輯發生在處理http請求之後,http處理請求是發生在RealCallexcute()處理的,下面看下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,簡單的調用鏈接圖如下:

Chain_Ainterceptor_AChain_Binterceptor_BChain_CCallServerInterceptorproceed()interceptor()proceed()interceptor()proceed()returnreturnreturnreturnreturnChain_Ainterceptor_AChain_Binterceptor_BChain_CCallServerInterceptor

  @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,形成處理鏈條,形成責任鏈模式。此外這裏還用到了模板方法模式,所謂模板方法模式,就是定義了一些固定的算法(執行步驟),然後提供某些待實現的抽象方法或者接口方法給其他子類或接口對象做擴展功能使用。開發中常常會遇到模板方法模式,比如ActivityFragment的基類等。

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中這樣使用責任鏈的好處是:

  • 符合開閉原則,擁抱改變,擴展性性更強
  • 符合類的單一原則,使類結構更加清晰
  • 用戶的可配置性強,可自由定製個性化需求,這對於一個開源框架來說是非常重要的
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章