大學(xué)上 UML 課程的時(shí)候總覺(jué)得設(shè)計(jì)模式遙不可及、無(wú)法理解,印象最深的是 UML 課程期末考試的最后一題是責(zé)任鏈模式相關(guān)的內(nèi)容睹簇,我沒(méi)有做出來(lái)掌眠。畢業(yè)后蕾盯,也曾抱著《設(shè)計(jì)模式·可復(fù)用面向?qū)ο筌浖幕A(chǔ)》從頭開(kāi)始看,總感覺(jué)一頭霧水蓝丙,少點(diǎn)什么级遭。
如今看來(lái)望拖,當(dāng)時(shí)還是見(jiàn)得太少,經(jīng)驗(yàn)不足挫鸽。
書中關(guān)于的責(zé)任鏈模式的主要例子是多視窗對(duì)用戶事件的處理说敏,Android 系統(tǒng)中 View 樹對(duì)觸摸事件的分發(fā)處理流程也用到了責(zé)任鏈模式。
該模式的核心想法是:
給多個(gè)對(duì)象處理一個(gè)請(qǐng)求的機(jī)會(huì)丢郊,從而解耦發(fā)送者和接受者盔沫,該請(qǐng)求沿著對(duì)象鏈傳遞直到其中一個(gè)對(duì)象處理它。
對(duì)于 Http 客戶端請(qǐng)求枫匾,兩個(gè)主要工作是處理緩存和網(wǎng)絡(luò)請(qǐng)求架诞,這兩部分都有機(jī)會(huì)處理 Http 請(qǐng)求,而且存在一種場(chǎng)景是離線狀態(tài)且緩存可用婿牍,這時(shí)的請(qǐng)求便只由緩存部分處理侈贷。采用責(zé)任鏈模式可以很優(yōu)雅地將緩存和網(wǎng)絡(luò)職責(zé)分離,而且 Cache 部分總是先于 Socket 處理請(qǐng)求等脂。
源碼
官網(wǎng)給的例子如下:
OkHttpClient client = new OkHttpClient();
String run(String url) throws IOException {
Request request = new Request.Builder()
.url(url)
.build();
Response response = client.newCall(request).execute();
return response.body().string();
}
我看文章的時(shí)候總是很討厭大段大段的代碼俏蛮,因?yàn)槲易约簳?huì)把源碼打開(kāi),邊看邊理解上遥。但為了文章的完整性搏屑,不得不放一點(diǎn)源碼。
我們只看同步執(zhí)行的過(guò)程:
@Override public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
try {
client.dispatcher().executed(this);
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} catch (IOException 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);
}
在這里我們可以清楚地看到整條鏈?zhǔn)接?Interceptor 數(shù)組構(gòu)成粉楚,依次是開(kāi)發(fā)者自定義的普通 Interceptor辣恋、處理 Cookie的 Interceptor、處理 Cache 的 Interceptor模软、創(chuàng)建連接的 Interceptor伟骨、開(kāi)發(fā)者自定義的網(wǎng)絡(luò) Interceptor(可以用于添加下載進(jìn)度功能)、真正發(fā)起網(wǎng)絡(luò)請(qǐng)求的 Interceptor燃异。
處理請(qǐng)求的傳遞的實(shí)現(xiàn)部分在RealInterceptorChain類里:
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection) throws IOException {
// 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;
}
每個(gè) Interceptor 里的 intercept 函數(shù)會(huì)攔截請(qǐng)求携狭,做自己的工作,然后調(diào)用 Chain 里的 proceed 函數(shù)回俐,將 index 增 1逛腿,傳遞 Request 給下一個(gè)攔截器處理。
CallServerInterceptor 是個(gè)例外仅颇,由于是整條處理鏈的最后一個(gè)攔截器单默,這里直接處理網(wǎng)絡(luò)請(qǐng)求,返回 Response忘瓦,不再繼續(xù)傳遞搁廓。
值得一提的是 CacheInterceptor 類對(duì) Http 協(xié)議的緩存設(shè)計(jì)做了完整的實(shí)現(xiàn),很適合拿來(lái)學(xué)習(xí)Http協(xié)議的緩存處理,具體的細(xì)節(jié)便不在這里細(xì)說(shuō)了枚抵。
小結(jié)
OkHttp 源碼讀起來(lái)確實(shí)很優(yōu)雅线欲,很順暢明场,但是如果是我們面對(duì)Http客戶端的實(shí)現(xiàn)需求汽摹,是否能想到這么優(yōu)雅的設(shè)計(jì)方案。和理解原理比起來(lái)苦锨,僅僅會(huì)使用庫(kù)還遠(yuǎn)遠(yuǎn)不夠逼泣;和設(shè)計(jì)實(shí)現(xiàn)比起來(lái),僅僅理解原理也同樣遠(yuǎn)遠(yuǎn)不夠舟舒。