24種設(shè)計(jì)模式之行為模式(一)- 職責(zé)鏈模式

實(shí)戰(zhàn)案例: ** 采購單的分級(jí)審批**

Sunny軟件公司程節(jié)了一個(gè)采購審批子系統(tǒng),其采購審批是分級(jí)進(jìn)行的,根據(jù)不同的采購金額由不同層次的主管進(jìn)行審批。主任審批5萬一下,副董事長(zhǎng)審批5萬到10萬。董事長(zhǎng)審批10萬-50萬的采購單。其流程如圖所示:

采購流程

在實(shí)現(xiàn)分級(jí)審批的時(shí)候,小明開發(fā)元提出了一個(gè)初始解決方案,使用PurchaseRequestHandler統(tǒng)一處理采購單,框架代碼如下:

Class PurchaseRequestHandler{
  public void sendRequestToDirectror(PurchaseRequest request){
    if(request.getAmount()<50000)
      //主管審批
      this.handlerByDirector(request);
    else if(request.getAmount()<100000)
      //副董事長(zhǎng)審批采購
      this.handlerByVicePresident(request);
......//省略一大堆
  }
}

通過上述代碼,我們分析一下,會(huì)有這幾個(gè)問題:

  1. 如果審批流程更長(zhǎng),會(huì)導(dǎo)致該類代碼越來越多,且各個(gè)級(jí)別的審批都在一個(gè)類中,違反了單一職責(zé)原則 ,測(cè)試維護(hù)難度大。

  2. 修改審批細(xì)節(jié)都必須對(duì)源代碼進(jìn)行修改并測(cè)試,違反了開閉原則。

  3. 審批流程缺乏靈活性,只能通過改源代碼實(shí)現(xiàn)流程的修改,無法動(dòng)態(tài)調(diào)整。
    上述問題想要解決,我們可以使用職責(zé)鏈模式,下面就讓我們打開新世界的大門吧。

職責(zé)鏈概述

職責(zé)鏈模式可以將請(qǐng)求的處理者組織成一條鏈,并讓請(qǐng)求沿著鏈傳遞,由鏈上的處理著對(duì)請(qǐng)求進(jìn)行處理,客戶端無需關(guān)心請(qǐng)求的處理細(xì)節(jié)以及請(qǐng)求的傳遞,只需將請(qǐng)求發(fā)送到鏈上即可,實(shí)現(xiàn)請(qǐng)求發(fā)送者和請(qǐng)求處理者的解耦。

定義:職責(zé)鏈模式(Chain of Responsibility Pattern):避免請(qǐng)求發(fā)送者與接受者耦合在一起,讓多個(gè)對(duì)象都有可能接收請(qǐng)求,將這些對(duì)象連接成一條鏈,并且沿著這條鏈傳遞請(qǐng)求,知道有對(duì)象處理它為止。職責(zé)鏈模式是一種對(duì)象行為模式。

職責(zé)鏈模式結(jié)構(gòu)的核心在于引入了抽象處理者:如圖:

職責(zé)鏈模式結(jié)構(gòu)圖

職責(zé)鏈模式結(jié)構(gòu)圖中包含如下幾個(gè)角色:

  • Handler: 定義了處理請(qǐng)求的接口,一般設(shè)計(jì)為抽象類,由具體不同的具體處理著處理請(qǐng)求的方式不同,因此定義抽象請(qǐng)求處理方法。因?yàn)槊總€(gè)處理者的下家還是一個(gè)處理者,因此在抽象處理者定義了一個(gè)抽象處理者類型的對(duì)象,作為下家引用。通過該引用,處理著可以連成一條鏈。
  • ConcreteHandler(具體處理者):他是抽象處理者的子類,可以處理用戶請(qǐng)求,在具體處理者類中實(shí)現(xiàn)了抽象處理者定義的抽象請(qǐng)求處理方法,在處理請(qǐng)求之前進(jìn)行判斷,看是否有相應(yīng)的處理權(quán)限,如果可以處理請(qǐng)求就處理它,否則轉(zhuǎn)發(fā)給后者;在具體處理者中可以訪問鏈中下一個(gè)對(duì)象,一遍請(qǐng)求的轉(zhuǎn)發(fā)。
    在職責(zé)鏈中:很多對(duì)象由每一個(gè)對(duì)象對(duì)其下家的引用而鏈接起來形成一條鏈。請(qǐng)求在這個(gè)鏈上傳遞,知道鏈上某個(gè)對(duì)象決定處理刺請(qǐng)求。發(fā)出這個(gè)請(qǐng)求的客戶端并不知道鏈上的哪一個(gè)對(duì)象最終處理這個(gè)請(qǐng)求,這使得系統(tǒng)可以在不影響客戶端的情況下動(dòng)態(tài)地重新組織鏈和分配責(zé)任。

純與不純的職責(zé)鏈模式

  1. 純的職責(zé)鏈模式:
    一個(gè)純的職責(zé)鏈模式要求一個(gè)具體處理者對(duì)象只能在兩個(gè)行為中選擇一個(gè):要么承擔(dān)全部責(zé) 任,要么將責(zé)任推給下家,不允許出現(xiàn)某一個(gè)具體處理者對(duì)象在承擔(dān)了一部分或全部責(zé)任后 又將責(zé)任向下傳遞的情況。而且在純的職責(zé)鏈模式中,要求一個(gè)請(qǐng)求必須被某一個(gè)處理者對(duì) 象所接收,不能出現(xiàn)某個(gè)請(qǐng)求未被任何一個(gè)處理者對(duì)象處理的情況。在前面的采購單審批實(shí) 例中應(yīng)用的是純的職責(zé)鏈模式。
  2. 不純的職責(zé)鏈模式:
    在一個(gè)不純的職責(zé)鏈模式中允許某個(gè)請(qǐng)求被一個(gè)具體處理者部分處理后再向下傳遞,或者一 個(gè)具體處理者處理完某請(qǐng)求后其后繼處理者可以繼續(xù)處理該請(qǐng)求,而且一個(gè)請(qǐng)求可以最終不 被任何處理者對(duì)象所接收。Java AWT 1.0中的事件處理模型應(yīng)用的是不純的職責(zé)鏈模式,其基 本原理如下:由于窗口組件(如按鈕、文本框等)一般都位于容器組件中,因此當(dāng)事件發(fā)生 在某一個(gè)組件上時(shí),先通過組件對(duì)象的handleEvent()方法將事件傳遞給相應(yīng)的事件處理方法, 該事件處理方法將處理此事件,然后決定是否將該事件向上一級(jí)容器組件傳播;上級(jí)容器組 件在接到事件之后可以繼續(xù)處理此事件并決定是否繼續(xù)向上級(jí)容器組件傳播,如此反復(fù),直 到事件到達(dá)頂層容器組件為止;如果一直傳到最頂層容器仍沒有處理方法,則該事件不予處 理。每一級(jí)組件在接收到事件時(shí),都可以處理此事件,而不論此事件是否在上一級(jí)已得到處 理,還存在事件未被處理的情況。顯然,這就是不純的職責(zé)鏈模式,早期的Java AWT事件模 型(JDK 1.0及更早)中的這種事件處理機(jī)制又叫事件浮升(Event Bubbling)機(jī)制。從Java.1.1以 后,JDK使用觀察者模式代替職責(zé)鏈模式來處理事件。目前,在JavaScript中仍然可以使用這 種事件浮升機(jī)制來進(jìn)行事件處理。

職責(zé)鏈模式總結(jié)

職責(zé)鏈模式通過建立一條鏈來組織請(qǐng)求的處理者,請(qǐng)求將沿著鏈進(jìn)行傳遞,請(qǐng)求發(fā)送者無須 知道請(qǐng)求在何時(shí)、何處以及如何被處理,實(shí)現(xiàn)了請(qǐng)求發(fā)送者與處理者的解耦。在軟件開發(fā) 中,如果遇到有多個(gè)對(duì)象可以處理同一請(qǐng)求時(shí)可以應(yīng)用職責(zé)鏈模式,例如在Web應(yīng)用開發(fā)中創(chuàng) 建一個(gè)過濾器(Filter)鏈來對(duì)請(qǐng)求數(shù)據(jù)進(jìn)行過濾,在工作流系統(tǒng)中實(shí)現(xiàn)公文的分級(jí)審批等等,使 用職責(zé)鏈模式可以較好地解決此類問題。

職責(zé)鏈模式優(yōu)缺點(diǎn)

優(yōu)點(diǎn):

  • 職責(zé)鏈模式使得一個(gè)對(duì)象無須知道是其他哪一個(gè)對(duì)象處理其請(qǐng)求,對(duì)象僅需知道該請(qǐng)求會(huì) 被處理即可,接收者和發(fā)送者都沒有對(duì)方的明確信息,且鏈中的對(duì)象不需要知道鏈的結(jié)構(gòu), 由客戶端負(fù)責(zé)鏈的創(chuàng)建,降低了系統(tǒng)的耦合度。
  • 請(qǐng)求處理對(duì)象僅需維持一個(gè)指向其后繼者的引用,而不需要維持它對(duì)所有的候選處理者的 引用,可簡(jiǎn)化對(duì)象的相互連接。
  • 在給對(duì)象分派職責(zé)時(shí),職責(zé)鏈可以給我們更多的靈活性,可以通過在運(yùn)行時(shí)對(duì)該鏈進(jìn)行動(dòng) 態(tài)的增加或修改來增加或改變處理一個(gè)請(qǐng)求的職責(zé)。
  • 在系統(tǒng)中增加一個(gè)新的具體請(qǐng)求處理者時(shí)無須修改原有系統(tǒng)的代碼,只需要在客戶端重新 建鏈即可,從這一點(diǎn)來看是符合“開閉原則”的。

缺點(diǎn):

  • 由于一個(gè)請(qǐng)求沒有明確的接收者,那么就不能保證它一定會(huì)被處理,該請(qǐng)求可能一直到鏈 的末端都得不到處理;一個(gè)請(qǐng)求也可能因職責(zé)鏈沒有被正確配置而得不到處理。
  • 對(duì)于比較長(zhǎng)的職責(zé)鏈,請(qǐng)求的處理可能涉及到多個(gè)處理對(duì)象,系統(tǒng)性能將受到一定影響, 而且在進(jìn)行代碼調(diào)試時(shí)不太方便。
  • 如果建鏈不當(dāng),可能會(huì)造成循環(huán)調(diào)用,將導(dǎo)致系統(tǒng)陷入死循環(huán)。

適用場(chǎng)景

  • 有多個(gè)對(duì)象可以處理同一個(gè)請(qǐng)求,具體哪個(gè)對(duì)象處理該請(qǐng)求待運(yùn)行時(shí)刻再確定,客戶端只 需將請(qǐng)求提交到鏈上,而無須關(guān)心請(qǐng)求的處理對(duì)象是誰以及它是如何處理的。
  • 在不明確指定接收者的情況下,向多個(gè)對(duì)象中的一個(gè)提交一個(gè)請(qǐng)求。
  • 可動(dòng)態(tài)指定一組對(duì)象處理請(qǐng)求,客戶端可以動(dòng)態(tài)創(chuàng)建職責(zé)鏈來處理請(qǐng)求,還可以改變鏈中 處理者之間的先后次序。
最后編輯于
?著作權(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ù)。

相關(guān)閱讀更多精彩內(nèi)容

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