責(zé)任鏈模式定義
百度上的定義:在責(zé)任鏈模式里,很多對(duì)象由每一個(gè)對(duì)象對(duì)其下家的引用而連接起來(lái)形成一條鏈。請(qǐng)求在這個(gè)鏈上傳遞,直到鏈上的某一個(gè)對(duì)象決定處理此請(qǐng)求。發(fā)出這個(gè)請(qǐng)求的客戶端并不知道鏈上的哪一個(gè)對(duì)象最終處理這個(gè)請(qǐng)求,這使得系統(tǒng)可以在不影響客戶端的情況下動(dòng)態(tài)地重新組織和分配責(zé)任。
設(shè)計(jì)模式書解析與實(shí)戰(zhàn)一書上的定義:使多個(gè)對(duì)象都有機(jī)會(huì)處理請(qǐng)求,從而避免了請(qǐng)求的發(fā)送者和接收者之間的耦合關(guān)系。將這些對(duì)象連成一條鏈,并沿著這條鏈傳遞該請(qǐng)求,直到有對(duì)象處理它為止。
通俗點(diǎn)講:顧名思義,責(zé)任鏈就是把處理者串成一條鏈,各個(gè)處理者為鏈上的各個(gè)節(jié)點(diǎn),每個(gè)節(jié)點(diǎn)都有各自的責(zé)任及處理方式。當(dāng)有事件請(qǐng)求時(shí),第一個(gè)鏈節(jié)點(diǎn)的處理者看自己是否要處理,如果要的話就自己處理,如果不要的話就交給下一個(gè)節(jié)點(diǎn)的處理者去處理。以此類推,直到最后一個(gè)節(jié)點(diǎn)的處理者
大致示意圖

舉例:
1、建議
雇員提了一個(gè)公司的建議給主管
主管若認(rèn)同這個(gè)建議,則繼續(xù)上報(bào)給經(jīng)理,否則直接批駁此建議
經(jīng)理收到建議,也同樣認(rèn)同覺(jué)得還不錯(cuò),則繼續(xù)上報(bào)給董事長(zhǎng),否則直接批駁此建議
董事長(zhǎng)也認(rèn)同此建議,則執(zhí)行,不認(rèn)同則駁回建議
2、請(qǐng)假
學(xué)生去跟老師請(qǐng)假
如果請(qǐng)假一天內(nèi),班導(dǎo)師可以直接處理,如果超過(guò)1天,則交由教務(wù)處輔導(dǎo)員來(lái)處理
如果請(qǐng)假三天內(nèi),輔導(dǎo)員可以處理,超過(guò)3天,則要由院長(zhǎng)來(lái)處理
如果院長(zhǎng)同意,則審批通過(guò),如果不同意,則打回請(qǐng)假請(qǐng)求
責(zé)任鏈模式的顯著特點(diǎn)
1、請(qǐng)求由起始點(diǎn),順著鏈路,依次向下傳遞,直至傳遞到最后一個(gè)處理者,所有的處理者都可以共享到這個(gè)請(qǐng)求體
2、處理者并不關(guān)心上一下或下一個(gè)處理者,只需要將當(dāng)前處理進(jìn)度組織成處理者遵循的接口規(guī)范即可,且確保處理者的響應(yīng)也遵循統(tǒng)一的接口規(guī)范
責(zé)任鏈設(shè)計(jì)思想
1、由于一個(gè)請(qǐng)求會(huì)被多個(gè)處理者進(jìn)行處理,這就需要請(qǐng)求的處理者在接收請(qǐng)求時(shí),需要遵循指定的接口規(guī)范。即需要對(duì)處理者進(jìn)行統(tǒng)一接口規(guī)則定義
2、由于責(zé)任鏈模式中,當(dāng)前處理者對(duì)請(qǐng)求進(jìn)行處理后,會(huì)將響應(yīng)返回。因此,這就需要所有的處理者返回響應(yīng)頭,也要遵循統(tǒng)一的接口規(guī)范。
3、請(qǐng)求處理者可能會(huì)存在多個(gè),具體處理者并不能確定,而且處理者的執(zhí)行順序不同,常發(fā)生在流程改變,或某個(gè)流程規(guī)則發(fā)生替換時(shí),因此這就需要有一個(gè)容器,將所有的請(qǐng)求處理者進(jìn)行保存,并且這個(gè)窗口需要由客戶端傳遞過(guò)來(lái)。此外還要確保容器的數(shù)據(jù)類型,定義為處理者接口,只有這樣,容器中才可以添加不同類型的處理者
4、最后還要確保實(shí)現(xiàn)鏈?zhǔn)秸{(diào)用,使窗口中記錄的請(qǐng)求處理者,根據(jù)請(qǐng)求,依次通過(guò)不同的請(qǐng)求處理者來(lái)處理請(qǐng)求。所以,接下來(lái)還需要提供一個(gè)專門用于執(zhí)行請(qǐng)求處理者的執(zhí)行鏈。
執(zhí)行鏈需要記錄客戶端指定的所有請(qǐng)求處理者,并記錄當(dāng)前請(qǐng)求處理者的光標(biāo)索引,同時(shí)還需要記錄客戶端請(qǐng)求。這樣一來(lái),當(dāng)執(zhí)行鏈被觸發(fā)時(shí),會(huì)在觸發(fā)調(diào)用的方法中,再次創(chuàng)建一個(gè)執(zhí)行鏈next,并為這個(gè)next定義光標(biāo)值+1,這就會(huì)使next這個(gè)執(zhí)行鏈被執(zhí)行時(shí),從處理者容器中獲取下一個(gè)處理者并將其執(zhí)行,而這下一個(gè)處理者,會(huì)再次創(chuàng)建一個(gè)執(zhí)行鏈,并在此基礎(chǔ)上更新光標(biāo)數(shù)值。這樣一來(lái),執(zhí)行鏈會(huì)被多次創(chuàng)建,而每次創(chuàng)建執(zhí)行時(shí),都會(huì)訪問(wèn)獲取處理者容器中的下一個(gè)元素,并將該元素執(zhí)行,這樣就將容器中的所有處理者,以鏈的方式一一執(zhí)行。每個(gè)處理者元素,都將會(huì)返回響應(yīng),由于這時(shí)已經(jīng)確保所有處理者按照在容器中的順序一一執(zhí)行,因此,按照順序?qū)㈨憫?yīng)進(jìn)行拼接,這就形成了客戶端請(qǐng)求按照順序,逐一被請(qǐng)求處理者執(zhí)行,并最終將響應(yīng)結(jié)果拼接,從而就實(shí)現(xiàn)了通過(guò)責(zé)任鏈模式的鏈?zhǔn)秸{(diào)用。
應(yīng)用場(chǎng)景
1、多個(gè)對(duì)象可以處理同一請(qǐng)求,但具體由哪個(gè)對(duì)象處理或是由多個(gè)對(duì)象共同處理則在運(yùn)行時(shí)動(dòng)態(tài)決定
2、請(qǐng)求處理環(huán)節(jié)會(huì)發(fā)生改變,導(dǎo)致其中的環(huán)節(jié)也有可能發(fā)生改變,也有可能所有的環(huán)節(jié)都會(huì)改變更新。
3、需要?jiǎng)討B(tài)指定一組對(duì)象處理請(qǐng)求
責(zé)任鏈類型
1、純的責(zé)任鏈模式:一個(gè)請(qǐng)求必須被某一個(gè)處理者接收,而且一個(gè)處理者對(duì)某個(gè)請(qǐng)求的處理只以采取自己處理,或推給下一個(gè)處理者來(lái)處理。
2、不純的責(zé)任鏈模式:允許出現(xiàn)某一個(gè)處理者在接收承擔(dān)了一部分責(zé)任(即已經(jīng)做了一些處理)后,再把請(qǐng)求傳給下一個(gè)處理者去進(jìn)行處理,且這個(gè)請(qǐng)求最終可以不被任意處理者接收
優(yōu)缺點(diǎn)
優(yōu)點(diǎn):
1、解耦了請(qǐng)求與處理
2、處理者只需要關(guān)注自己職責(zé)需要處理的請(qǐng)求即可,對(duì)于不是職責(zé)需要的請(qǐng)求,直接轉(zhuǎn)給下一節(jié)點(diǎn)處理對(duì)象
3、鏈?zhǔn)絺鬟f處理請(qǐng)求,請(qǐng)求者無(wú)需關(guān)注鏈路處理者結(jié)構(gòu),只需要等待處理結(jié)果就好
4、鏈?zhǔn)浇Y(jié)構(gòu)靈活,可以通過(guò)鏈路結(jié)構(gòu)動(dòng)態(tài)地新增或刪減處理者
5、易于擴(kuò)展新的處理對(duì)象,符合開(kāi)閉原則(即對(duì)于擴(kuò)展是開(kāi)放的,但是對(duì)于修改是封閉的)
缺點(diǎn):
1、鏈路過(guò)長(zhǎng)的時(shí)候,會(huì)影響請(qǐng)求傳遞的處理效率,損耗性能
2、如果節(jié)點(diǎn)對(duì)象存在循環(huán)引用時(shí),會(huì)造成死循環(huán),導(dǎo)致崩潰
3、請(qǐng)求不一定會(huì)被處理
代碼實(shí)現(xiàn)
簡(jiǎn)陋地實(shí)現(xiàn)學(xué)生請(qǐng)假責(zé)任鏈流程
1、定義攔截器抽象接口
interface IHandler {
/**
* 攔截操作 每個(gè)攔截器將會(huì)在此方法中根據(jù)攔截鏈Chain來(lái)觸發(fā)下一個(gè)攔截器的調(diào)用,
* 直至最后一個(gè)才不進(jìn)行觸發(fā)
*
* @return 每個(gè)攔截器處理的結(jié)果
*/
fun intercept(chain: Chain) : String
/**
* 攔截鏈抽象接口
*/
interface Chain {
/**
* 請(qǐng)求
*/
fun request() : Request
/**
* 處理請(qǐng)求
*/
fun process() : String
}
}
2、實(shí)現(xiàn)攔截鏈
open class RealChain(
private val handlers: ArrayList<IHandler>,
private val index: Int,
private val request: Request
) : IHandler.Chain{
override fun request(): Request {
return request
}
/**
* 在process方法中,每次調(diào)用該方法時(shí)都會(huì)創(chuàng)建一個(gè)RealChain對(duì)象,并根據(jù)當(dāng)前index光標(biāo)索引,獲取當(dāng)前攔截器,執(zhí)行intercept方法,并傳入下一個(gè)next
* 這樣一來(lái)就可以在具體的IHandler處理攔截器中,來(lái)決定是否需要繼續(xù)調(diào)用RealChain對(duì)象的process方法,如果繼續(xù)調(diào)用此方法,則框架會(huì)繼續(xù)執(zhí)行下一個(gè)節(jié)點(diǎn)的攔截操作
* 否則,將會(huì)終止,并返回結(jié)果
*/
override fun process() : String {
if (index > handlers.size - 1) {
return "無(wú)人審批!"
}
// 生成下一個(gè)鏈節(jié)點(diǎn)
val next = RealChain(handlers, index + 1, request)
// 執(zhí)行當(dāng)前的請(qǐng)求任務(wù),并傳入到下一個(gè)處理者中
// 從面使攔截鏈可以執(zhí)行interceptors容器中的下一個(gè)攔截器
val handler = handlers[index]
return handler.intercept(next)
}
}
3、實(shí)現(xiàn)具體的攔截處理器
class TeacherHandler : IHandler {
override fun intercept(chain: IHandler.Chain) : String {
val request = chain.request()
return if (request.day <= 1) {
"班導(dǎo)覺(jué)得 ${request.reason} 理由充分,可以批準(zhǔn)請(qǐng)假"
} else {
"班導(dǎo)處理不了,交給下一級(jí)處理\n${chain.process()}"
}
}
}
班導(dǎo)處理器模擬了班導(dǎo)師審核請(qǐng)假時(shí)候的操作,自己不能處理時(shí),通過(guò)RealChain使下一個(gè)處理器執(zhí)行
class CounselorHandler : IHandler {
override fun intercept(chain: IHandler.Chain) : String {
val request = chain.request()
return if (request.day in 2..3) {
"輔導(dǎo)員覺(jué)得 ${request.reason} 理由充分,批準(zhǔn)了請(qǐng)假"
} else {
"輔導(dǎo)員處理不了,交給下一級(jí)處理\n${chain.process()}"
}
}
}
輔導(dǎo)員處理器判斷自己是否處理請(qǐng)求,或不處理,繼續(xù)通過(guò)RealChain來(lái)使下一個(gè)處理器執(zhí)行
class PresidentHandler : IHandler {
override fun intercept(chain: IHandler.Chain) : String {
return if (chain.request().day < 11) {
"院長(zhǎng)覺(jué)得 ${chain.request().reason} 理由充分,批準(zhǔn)了請(qǐng)假"
} else {
"院長(zhǎng)覺(jué)得請(qǐng)假${chain.request().day}天時(shí)間太長(zhǎng),不批"
}
}
}
院長(zhǎng)處理器是最后一個(gè)處理器了,所以便不用再通過(guò)RealChain向下一個(gè)處理器派發(fā)請(qǐng)求了
4、開(kāi)始申請(qǐng),責(zé)任鏈調(diào)用
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
...
val handlers = arrayListOf<IHandler>()
handlers.add(TeacherHandler())
handlers.add(CounselorHandler())
handlers.add(PresidentHandler())
val chain = RealChain(handlers, 0, Request(day = 3, reason = "不想上課"))
println("請(qǐng)假審批開(kāi)始")
println(chain.process())
}
}
5、結(jié)果
I/System.out: 請(qǐng)假審批開(kāi)始
I/System.out: 班導(dǎo)處理不了,交給下一級(jí)處理
I/System.out: 輔導(dǎo)員處理不了,交給下一級(jí)處理
I/System.out: 院長(zhǎng)覺(jué)得 不想上課 理由充分,批準(zhǔn)了請(qǐng)假
責(zé)任鏈模式在okhttp上使用 - 攔截器
okhttp中的攔截器,一個(gè)網(wǎng)絡(luò)請(qǐng)求,按一定的順序,經(jīng)由多個(gè)攔截器進(jìn)行處理,該攔截器可以決定自己處理并且返回處理結(jié)果,也可以選擇向下繼續(xù)傳遞,讓后面的攔截器處理返回它的結(jié)果。
首先我們來(lái)看下同步情況下的流程,請(qǐng)求會(huì)走到RealCall execute()方法里邊
override fun execute(): Response {
// 同一個(gè)call只能被執(zhí)行一次
synchronized(this) {
check(!executed) { "Already Executed" }
executed = true
}
transmitter.timeoutEnter()
transmitter.callStart()
try {
// 將call添加到調(diào)度器里邊
client.dispatcher.executed(this)
// 核心方法,責(zé)任鏈獲取到Response結(jié)果
return getResponseWithInterceptorChain()
} finally {
client.dispatcher.finished(this)
}
}
@Throws(IOException::class)
fun getResponseWithInterceptorChain(): Response {
// 添加處理攔截器
val interceptors = mutableListOf<Interceptor>()
interceptors += client.interceptors
interceptors += RetryAndFollowUpInterceptor(client)
interceptors += BridgeInterceptor(client.cookieJar)
interceptors += CacheInterceptor(client.cache)
interceptors += ConnectInterceptor
if (!forWebSocket) {
interceptors += client.networkInterceptors
}
interceptors += CallServerInterceptor(forWebSocket)
// 責(zé)任鏈 創(chuàng)建攔截鏈實(shí)例 將上面的攔截器添加到責(zé)任鏈里面
val chain = RealInterceptorChain(interceptors, transmitter, null, 0, originalRequest, this,
client.connectTimeoutMillis, client.readTimeoutMillis, client.writeTimeoutMillis)
...
val response = chain.proceed(originalRequest)
...
}
可以看到,getResponseWithInterceptorChain()方法,是將自定義的攔截器以及默認(rèn)的幾個(gè)攔截器添加到攔截鏈中去,然后執(zhí)行鏈中的處理方法proceed,一直往下調(diào)
再看看proceed()方法
@Throws(IOException::class)
fun proceed(request: Request, transmitter: Transmitter, exchange: Exchange?): Response {
...
// 責(zé)任鏈 把index + 1,創(chuàng)建下一個(gè)RealInterCeptorChain節(jié)點(diǎn)對(duì)象
val next = RealInterceptorChain(interceptors, transmitter, exchange,
index + 1, request, call, connectTimeout, readTimeout, writeTimeout)
val interceptor = interceptors[index]
@Suppress("USELESS_ELVIS")
// 執(zhí)行默認(rèn)的攔截器
val response = interceptor.intercept(next) ?: throw NullPointerException(
"interceptor $interceptor returned null")
...
return response
}
從這也可以看出chain.proceed()方法是如何進(jìn)行鏈?zhǔn)絺鬟f的。類似遞歸,每次進(jìn)行RealInterceptorChain(index + 1),使當(dāng)前攔截器擁有下一個(gè)攔截鏈節(jié)點(diǎn)的引用,那當(dāng)前攔截器就可以通過(guò)這個(gè)引用調(diào)用下一個(gè)chain.proceed()方法來(lái)傳遞請(qǐng)求了
可以看一下默認(rèn)的RetryAndFollowUpInterceptor攔截器
class RetryAndFollowUpInterceptor(private val client: OkHttpClient) : Interceptor {
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
...
//調(diào)用proceed(),傳遞請(qǐng)求到下一個(gè)攔截器
response = realChain.proceed(request, transmitter, null)
...
}
如果還不是很清楚,可以看一下下面這張圖,圖示相對(duì)直觀多了

參考
https://blog.csdn.net/qq_15274383/article/details/78485648
https://juejin.im/post/5d9cb062e51d45780c34a83e
https://blog.csdn.net/sunyao19940708/article/details/81539767