責(zé)任鏈模式

責(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)的處理者

大致示意圖
image.png
舉例:

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ì)直觀多了


image.png
參考

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容