【白話設計模式二十三】職責鏈模式(Chain of Responsibility)

#0 系列目錄#數據庫

#1 場景問題# ##1.1 申請聚餐費用## 來考慮這樣一個功能:申請聚餐費用的管理。編程

不少公司都有這樣的福利,就是項目組或者是部門能夠向公司申請一些聚餐費用,用於組織項目組成員或者是部門成員進行聚餐活動,以增進人員之間的情感,更有利於工做中的相互合做。設計模式

申請聚餐費用的大體流程通常是:由申請人先填寫申請單,而後交給領導審查,若是申請批准下來了,領導會通知申請人審批經過,而後申請人去財務核領費用,若是沒有核準,領導會通知申請人審批未經過,此事也就此做罷了。安全

不一樣級別的領導,對於審批的額度是不同的,好比:項目經理只能審批500元之內的申請;部門經理能審批1000元之內的申請;而總經理能夠審覈任意額度的申請。框架

也就是說,當某人提出聚餐費用申請的請求後,該請求會由項目經理、部門經理、總經理之中的某一位領導來進行相應的處理,可是提出申請的人並不知道最終會由誰來處理他的請求,通常申請人是把本身的申請提交給項目經理,或許最後是由總經理來處理他的請求,可是申請人並不知道應該由總經理來處理他的申請請求。測試

那麼該怎樣實現這樣的功能呢?ui

##1.2 不用模式的解決方案## 分析上面要實現的功能,主要就是要根據申請費用的多少,而後讓不一樣的領導來進行處理就能夠實現了。也就是有點邏輯判斷而已,示例代碼以下:this

/**
 * 處理聚餐費用申請的對象
 */
public class FeeRequest {
    /**
     * 提交聚餐費用申請給項目經理
     * @param user 申請人
     * @param fee 申請費用
     * @return 成功或失敗的具體通知
     */
    public String requestToProjectManager(String user,double fee){
        String str = "";
        if(fee < 500){
            //項目經理的權限比較小,只能在500之內
            str = this.projectHandle(user, fee);
        }else if(fee < 1000){
            //部門經理的權限只能在1000之內
            str = this.depManagerHandle(user, fee);
        }else if(fee >= 1000){
            //總經理的權限很大,只要請求到了這裏,他均可以處理
            str = this.generalManagerHandle(user, fee);
        }
        return str;
    }
    /**
     * 項目經理審批費用申請,參數、返回值和上面是同樣的,省略了
     */
    private String projectHandle(String user, double fee) {
        String str = "";
        //爲了測試,簡單點,只贊成小李的
        if("小李".equals(user)){
            str = "項目經理贊成"+user+"聚餐費用"+fee+"元的請求";
        }else{
            //其它人一概不一樣意
            str = "項目經理不一樣意"+user+"聚餐費用"+fee+"元的請求";
        }
        return str;
    }
    /**
     * 部門經理審批費用申請,參數、返回值和上面是同樣的,省略了
     */
    private String depManagerHandle(String user, double fee) {
        String str = "";
        //爲了測試,簡單點,只贊成小李申請的
        if("小李".equals(user)){
            str = "部門經理贊成"+user+"聚餐費用"+fee+"元的請求";
        }else{
            //其它人一概不一樣意
            str= "部門經理不一樣意"+user+"聚餐費用"+fee+"元的請求";
        }
        return str;
     }
    /**
     * 總經理審批費用申請,參數、返回值和上面是同樣的,省略了
     */
    private String generalManagerHandle(String user, double fee) {
       String str = "";
      
       //爲了測試,簡單點,只贊成小李的
       if("小李".equals(user)){
           str = "總經理贊成"+user+"聚餐費用"+fee+"元的請求";
       }else{
           //其它人一概不一樣意
           str = "總經理不一樣意"+user+"聚餐費用"+fee+"元的請求";
       }
       return str;
    }
}

寫個客戶端來測試看看效果,示例代碼以下:.net

public class Client {
    public static void main(String[] args) {
        FeeRequest request = new FeeRequest();
      
        //開始測試
        String ret1 = request.requestToProjectManager("小李", 300);
        System.out.println("the ret="+ret1);     
        String ret2 = request.requestToProjectManager("小張", 300);
        System.out.println("the ret="+ret2);
      
        String ret3 = request.requestToProjectManager("小李", 600);
        System.out.println("the ret="+ret3);     
        String ret4 = request.requestToProjectManager("小張", 600);
        System.out.println("the ret="+ret4);
      
        String ret5 = request.requestToProjectManager("小李", 1200);
        System.out.println("the ret="+ret5);     
        String ret6 = request.requestToProjectManager("小張", 1200);
        System.out.println("the ret="+ret6);
    }
}

運行結果以下:設計

the ret1=項目經理贊成小李聚餐費用300.0元的請求
the ret2=項目經理不一樣意小張聚餐費用300.0元的請求
the ret3=部門經理贊成小李聚餐費用600.0元的請求
the ret4=部門經理不一樣意小張聚餐費用600.0元的請求
the ret5=總經理贊成小李聚餐費用1200.0元的請求
the ret6=總經理不一樣意小張聚餐費用1200.0元的請求

##1.3 有何問題## 上面的實現很簡單,基本上沒有什麼特別的難度。仔細想一想,這麼實現有沒有問題呢?仔細分析申請聚餐費用的業務功能和目前的實現,主要面臨着以下問題:

聚餐費用申請的處理流程是可能會變更的。

好比如今的處理流程是:提交申請給項目經理,看看是否適合由項目經理處理,若是不是,看看是否適合由部門經理處理,若是不是,看看是否適合由總經理處理的步驟。從此可能會變化成:直接提交給部門經理,看看是否適合由部門經理處理,若是不是,總經理處理這樣的步驟。也就是說,對於聚餐費用申請,要求處理的邏輯步驟是靈活的。

各個處理環節的業務處理也是會變更的。

由於處理流程可能發生變化,也會致使某些步驟的具體的業務功能發生變化,好比:本來部門經理審批聚餐費用的時候,只是判斷是否批准;如今,部門經理可能在審批聚餐費用的時候,覈算本部門的實時成本,這就出現新的業務處理功能了。

若是採用上面的實現,要是處理的邏輯發生了變化,解決的方法,一個是生成一個子類,覆蓋requestToProjectManager方法,而後在裏面實現新的處理;另一個方法就是修改處理申請的方法的源代碼來實現要是具體處理環節的業務處理的功能發生了變化,那就只好找到相應的處理方法,進行源代碼修改了

總之都不是什麼好方法,也就是說,若是出現聚餐費用申請的處理流程變化的狀況,或者是出現各個處理環節的功能變化的時候,上面的實現方式是很難靈活的變化來適應新功能的要求的。

把上面的問題抽象一下:客戶端發出一個請求,會有不少對象均可以來處理這個請求,並且不一樣對象的處理邏輯是不同的。對於客戶端而言,無所謂誰來處理,反正有對象處理就能夠了。

並且在上述處理中,還但願處理流程是能夠靈活變更的,而處理請求的對象須要能方便的修改或者是被替換掉,以適應新的業務功能的須要。

請問如何才能實現上述要求?

#2 解決方案# ##2.1 職責鏈模式來解決## 用來解決上述問題的一個合理的解決方案,就是使用職責鏈模式。那麼什麼是職責鏈模式呢?

  1. 職責鏈模式定義

輸入圖片說明

  1. 應用職責鏈模式來解決的思路

仔細分析上面的場景,當客戶端提出一個聚餐費用的申請,後續處理這個申請的對象,項目經理、部門經理和總經理,天然的造成了一個鏈,從項目經理-部門經理-總經理,客戶端的申請請求就在這個鏈中傳遞,直到有領導處理爲止。看起來,上面的功能要求很適合採用職責鏈來處理這個業務。

要想讓處理請求的流程能夠靈活的變更,一個基本的思路,那就是動態構建流程步驟,這樣隨時均可以從新組合出新的流程來。而要讓處理請求的對象也要很靈活,那就要讓它足夠簡單,最好是隻實現單一的功能,或者是有限的功能,這樣更有利於修改和複用

職責鏈模式就很好的體現了上述的基本思路,首先職責鏈模式會定義一個全部處理請求的對象都要繼承實現的抽象類,這樣就有利於隨時切換新的實現;其次每一個處理請求對象只實現業務流程中的一步業務處理,這樣使其變得簡單;最後職責鏈模式會動態的來組合這些處理請求的對象,把它們按照流程動態組合起來,並要求它們依次調用,這樣就動態的實現了流程。

這樣一來,若是流程發生了變化,只要從新組合就行了;若是某個處理的業務功能發生了變化,一個方案是修改該處理對應的處理對象,另外一個方案是直接提供一個新的實現,而後在組合流程的時候,用新的實現替換掉舊的實現就能夠了。

##2.2 模式結構和說明## 職責鏈模式的結構如圖23.1所示:

輸入圖片說明

Handler:定義職責的接口,一般在這裏定義處理請求的方法,能夠在這裏實現後繼鏈。

ConcreteHandler:實現職責的類,在這個類裏面,實現對在它職責範圍內請求的處理,若是不處理,就繼續轉發請求給後繼者。

Client:職責鏈的客戶端,向鏈上的具體處理者對象提交請求,讓職責鏈負責處理。

##2.3 職責鏈模式示例代碼##

  1. 先來看看職責的接口定義,示例代碼以下:
/**
 * 職責的接口,也就是處理請求的接口
 */
public abstract class Handler {
    /**
     * 持有後繼的職責對象
     */
    protected Handler successor;
    /**
     * 設置後繼的職責對象
     * @param successor 後繼的職責對象
     */
    public void setSuccessor(Handler successor) {
        this.successor = successor;
    }
    /**
     * 示意處理請求的方法,雖然這個示意方法是沒有傳入參數,
     * 但實際是能夠傳入參數的,根據具體須要來選擇是否傳遞參數
     */
    public abstract void handleRequest();
}
  1. 接下來看看具體的職責實現對象,示例代碼以下:
/**
 * 具體的職責對象,用來處理請求
 */
public class ConcreteHandler1 extends Handler {
    public void handleRequest() {
        //根據某些條件來判斷是否屬於本身處理的職責範圍
        //判斷條件好比:從外部傳入的參數,或者這裏主動去獲取的外部數據,
        //如從數據庫中獲取等,下面這句話只是個示意
        boolean someCondition = false;
     
        if(someCondition){
            //若是屬於本身處理的職責範圍,就在這裏處理請求
            //具體的處理代碼
            System.out.println("ConcreteHandler1 handle request");
        }else{
            //若是不屬於本身處理的職責範圍,那就判斷是否還有後繼的職責對象
            //若是有,就轉發請求給後繼的職責對象
            //若是沒有,什麼都不作,天然結束
            if(this.successor!=null){
                this.successor.handleRequest();
            }
        }
    }
}

另一個ConcreteHandler2和上面ConcreteHandler1的示意代碼幾乎是同樣的,所以就不去贅述了。

  1. 接下來看看客戶端的示意,示例代碼以下:
/**
 * 職責鏈的客戶端,這裏只是個示意
 */
public class Client {
    public static void main(String[] args) {
        //先要組裝職責鏈
        Handler h1 = new ConcreteHandler1();
        Handler h2 = new ConcreteHandler2();
     
        h1.setSuccessor(h2);    
        //而後提交請求
        h1.handleRequest();
    }
}

##2.4 使用職責鏈模式重寫示例## 要使用職責鏈模式來重寫示例,仍是先來實現以下的功能:當某人提出聚餐費用申請的請求後,該請求會在項目經理-部門經理-總經理這樣一條領導處理鏈上進行傳遞,發出請求的人並不知道誰會來處理他的請求,每一個領導會根據本身的職責範圍,來判斷是處理請求仍是把請求交給更高級的領導,只要有領導處理了,傳遞就結束了

須要把每位領導的處理獨立出來,實現成單獨的職責處理對象,而後爲它們提供一個公共的、抽象的父職責對象,這樣就能夠在客戶端來動態的組合職責鏈,實現不一樣的功能要求了。仍是看一下示例的總體結構,會有助於對示例的理解和把握。如圖23.2所示:

輸入圖片說明

  1. 定義職責的抽象類

首先來看看定義全部職責的抽象類,也就是全部職責的外觀,在這個類裏面持有下一個處理請求的對象,同時還要定義業務處理方法,示例代碼以下:

/**
 * 定義職責對象的接口
 */
public abstract class Handler {
    /**
     * 持有下一個處理請求的對象
     */
    protected Handler successor = null;
    /**
     * 設置下一個處理請求的對象
     * @param successor 下一個處理請求的對象
     */
    public void setSuccessor(Handler successor){
       this.successor = successor;
    }
    /**
     * 處理聚餐費用的申請
     * @param user 申請人
     * @param fee 申請的錢數
     * @return 成功或失敗的具體通知
     */
    public abstract String handleFeeRequest(String user,double fee);
}
  1. 實現各自的職責

如今實現的處理聚餐費用流程是:申請人提出的申請交給項目經理處理,項目經理的處理權限是500元之內,超過500元,把申請轉給部門經理處理,部門經理的處理權限是1000元之內,超過1000元,把申請轉給總經理處理。

分析上述流程,對請求主要有三個處理環節,把它們分別實現成爲職責對象,一個對象實現一個環節的處理功能,這樣就會比較簡單。

先看看項目經理的處理吧,示例代碼以下:

public class ProjectManager extends Handler{
    public String handleFeeRequest(String user, double fee) {
        String str = "";
        //項目經理的權限比較小,只能在500之內
        if(fee < 500){
            //爲了測試,簡單點,只贊成小李的
            if("小李".equals(user)){
                str = "項目經理贊成"+user+"聚餐費用"+fee+"元的請求";
            }else{
                //其它人一概不一樣意
                str = "項目經理不一樣意"+user+"聚餐費用"+fee+"元的請求";
            }
            return str;
        }else{
            //超過500,繼續傳遞給級別更高的人處理
            if(this.successor!=null){
                return successor.handleFeeRequest(user, fee);
            }
        }
        return str;
    }
}

接下來看看部門經理的處理,示例代碼以下:

public class DepManager extends Handler{ 
    public String handleFeeRequest(String user, double fee) {
        String str = "";
        //部門經理的權限只能在1000之內
        if(fee < 1000){
            //爲了測試,簡單點,只贊成小李申請的
            if("小李".equals(user)){
                str = "部門經理贊成"+user+"聚餐費用"+fee+"元的請求";
            }else{
                //其它人一概不一樣意
                str = "部門經理不一樣意"+user+"聚餐費用"+fee+"元的請求";
            }
            return str;
        }else{
            //超過1000,繼續傳遞給級別更高的人處理
            if(this.successor!=null){
                return this.successor.handleFeeRequest(user, fee);
            }
        }
        return str;
    }
}

再看總經理的處理,示例代碼以下:

public class GeneralManager extends Handler{
    public String handleFeeRequest(String user, double fee) {
        String str = "";
        //總經理的權限很大,只要請求到了這裏,他均可以處理
        if(fee >= 1000){
            //爲了測試,簡單點,只贊成小李的
            if("小李".equals(user)){
                str = "總經理贊成"+user+"聚餐費用"+fee+"元的請求";
            }else{
                //其它人一概不一樣意
                str = "總經理不一樣意"+user+"聚餐費用"+fee+"元的請求";
            }
            return str;
        }else{
            //若是還有後繼的處理對象,繼續傳遞
            if(this.successor!=null){
                return successor.handleFeeRequest(user, fee);
            }
        }
        return str;
    }
}
  1. 使用職責鏈

那麼客戶端如何使用職責鏈呢,最重要的就是要先構建職責鏈,而後才能使用。示例代碼以下:

public class Client {
    public static void main(String[] args) {
        //先要組裝職責鏈   
        Handler h1 = new GeneralManager();
        Handler h2 = new DepManager();
        Handler h3 = new ProjectManager();
        h3.setSuccessor(h2);
        h2.setSuccessor(h1);
     
        //開始測試
        String ret1 = h3.handleFeeRequest("小李", 300);
        System.out.println("the ret1="+ret1); 
        String ret2 = h3.handleFeeRequest("小張", 300);
        System.out.println("the ret2="+ret2); 
     
        String ret3 = h3.handleFeeRequest("小李", 600);
        System.out.println("the ret3="+ret3); 
        String ret4 = h3.handleFeeRequest("小張", 600);
        System.out.println("the ret4="+ret4); 
     
        String ret5 = h3.handleFeeRequest("小李", 1200); 
        System.out.println("the ret5="+ret5); 
        String ret6 = h3.handleFeeRequest("小張", 1200);
        System.out.println("the ret6="+ret6); 
    }
}

運行結果以下:

the ret1=項目經理贊成小李聚餐費用300.0元的請求
the ret2=項目經理不一樣意小張聚餐費用300.0元的請求
the ret3=部門經理贊成小李聚餐費用600.0元的請求
the ret4=部門經理不一樣意小張聚餐費用600.0元的請求
the ret5=總經理贊成小李聚餐費用1200.0元的請求
the ret6=總經理不一樣意小張聚餐費用1200.0元的請求

看起來結果跟前面不用模式的實現方案的運行結果是同樣的,它們原本就是實現的一樣的功能,只不過實現方式不一樣而已。

  1. 如何運行

理解了示例的總體結構和具體實現,那麼示例的具體運行過程是怎樣的呢?

下面就以「小李申請聚餐費用1200元」這個費用申請爲例來講明,調用過程的示意圖如圖23.3所示:

輸入圖片說明

#3 模式講解# ##3.1 認識職責鏈模式##

  1. 模式功能

職責鏈模式主要用來處理:「客戶端發出一個請求,有多個對象都有機會來處理這一個請求,可是客戶端不知道究竟誰會來處理他的請求」,這樣的狀況。也就是須要讓請求者和接收者解耦,這樣就能夠動態的切換和組合接收者了

要注意在標準的職責鏈模式裏面,是隻要有對象處理了請求,這個請求就到此爲止,再也不被傳遞和處理了。

若是是要變形使用職責鏈,就可讓這個請求繼續傳遞,每一個職責對象對這個請求進行必定的功能處理,從而造成一個處理請求的功能鏈。

  1. 隱式接收者

當客戶端發出請求的時候,客戶端並不知道誰會真正處理他的請求,客戶端只知道他提交請求的第一個對象。從第一個處理對象開始,整個職責鏈裏面的對象,要麼本身處理請求,要麼繼續轉發給下一個接收者。

也就是對於請求者而言,並不知道最終的接收者是誰,可是通常狀況下,老是會有一個對象來處理的,所以稱爲隱式接收者

  1. 如何構建鏈

職責鏈的鏈怎麼構建呢?這是個大問題,實現的方式也是五花八門,歸結起來大體有如下一些方式。

首先是按照實現的地方來講:

能夠實如今客戶端,在提交請求前組合鏈,也就是在使用的時候動態組合鏈,稱爲外部鏈;

也能夠在Handler裏面實現鏈的組合,算是內部鏈的一種;

固然還有一種就是在各個職責對象裏面,由各個職責對象自行決定後續的處理對象,這種實現方式要求每一個職責對象除了進行業務處理外,還必須瞭解整個業務流程。

按照構建鏈的數據來源,也就是決定了按照什麼順序來組合鏈的數據,又分爲幾種:

一種就是在程序裏面動態組合;

也能夠經過外部,好比數據庫來獲取組合的數據,這種屬於數據庫驅動的方式;

還有一種方式就是經過配置文件傳遞進來,也能夠是流程的配置文件;

若是是從外部獲取數據來構建鏈,那麼在程序運行的時候,會讀取這些數據,而後根據數據的要求來獲取相應的對象,並組合起來。

還有一種是不須要構建鏈,由於已有的對象已經天然構成鏈了,這種狀況多出如今組合模式構建的對象樹中,這樣子對象能夠很天然的向上找到本身的父對象。就像部門人員的組織結構同樣,頂層是總經理,總經理下面是各個部門的經理,部門經理下面是項目經理,項目經理下面是各個普通員工,天然就能夠造成:普通員工-項目經理-部門經理-總經理這樣的鏈。

  1. 誰來處理

職責鏈中那麼多處理對象,到底誰來處理請求呢,這個是在運行時期動態決定的。當請求被傳遞到某個處理對象的時候,這個對象會按照已經設定好的條件來判斷,是否屬於本身處理的範圍,若是是就處理,若是不是就轉發請求給下一個對象。

  1. 請求必定會被處理嗎

在職責鏈模式中,請求不必定會被處理,由於可能沒有合適的處理者,請求在職責鏈裏面從頭傳遞到尾,每一個處理對象都判斷不屬於本身處理,最後請求就沒有對象來處理。這一點是須要注意的。

能夠在職責鏈的末端始終加上一個不支持此功能處理的職責對象,這樣若是傳遞到這裏,就會出現提示,本職責鏈沒有對象處理這個請求。

##3.2 處理多種請求## 前面的示例都是同一個職責鏈處理一種請求的狀況,如今有這樣的需求,仍是費用申請的功能,此次是申請預支差旅費,假設仍是同一流程,也就是組合同一個職責鏈,從項目經理-傳遞給部門經理-傳遞給總經理,雖然流程相同,可是每一個處理類須要處理兩種請求,它們的具體業務邏輯是不同的,那麼該如何實現呢?

  1. 簡單的處理方式

要解決這個問題,也不是很困難,一個簡單的方法就是爲每種業務單獨定義一個方法,而後客戶端根據不一樣的須要調用不一樣的方法,仍是經過代碼來示例一下。注意這裏故意的把兩個方法作的有些不同,一個是返回String類型的值,一個是返回boolean類型的值另一個是返回到客戶端再輸出信息,一個是直接在職責處理裏面就輸出信息

(1)首先是改造職責對象的接口,添加上新的業務方法,示例代碼以下:

/**
 * 定義職責對象的接口
 */
public abstract class Handler {
    /**
     * 持有下一個處理請求的對象
     */
    protected Handler successor = null;
    /**
     * 設置下一個處理請求的對象
     * @param successor 下一個處理請求的對象
     */
    public void setSuccessor(Handler successor){
        this.successor = successor;
    }
    /**
     * 處理聚餐費用的申請
     * @param user 申請人
     * @param fee 申請的錢數
     * @return 成功或失敗的具體通知
     */
    public abstract String handleFeeRequest(String user,double fee);
    /**
     * 處理預支差旅費用的申請
     * @param user 申請人
     * @param requestFee 申請的錢數
     * @return 是否贊成
     */
    public abstract boolean handlePreFeeRequest(String user,double requestFee);
}

(2)職責的接口發生了改變,對應的處理類也要改變,這幾個處理類是相似的,原有的功能不變,而後在新的實現方法裏面,一樣判斷一下是否屬於本身處理的範圍,若是屬於本身處理的範圍那就處理,不然就傳遞到下一個處理。仍是示範一個,看看項目經理的處理吧,示例代碼以下:

public class ProjectManager extends Handler{
    public String handleFeeRequest(String user, double fee) {
        String str = "";
        //項目經理的權限比較小,只能在500之內
        if(fee < 500){
            //爲了測試,簡單點,只贊成小李的
            if("小李".equals(user)){
                str = "項目經理贊成"+user+"聚餐費用"+fee+"元的請求";
            }else{
               //其它人一概不一樣意
               str = "項目經理不一樣意"+user+"聚餐費用"+fee+"元的請求";
            }
            return str;
        }else{
            //超過500,繼續傳遞給級別更高的人處理
            if(this.successor!=null){
                return successor.handleFeeRequest(user, fee);
            }
        }
        return str;
    }
    public boolean handlePreFeeRequest(String user, double requestNum) {
        //項目經理的權限比較小,只能在5000之內
        if(requestNum < 5000){
            //工做須要嘛,通通贊成
            System.out.println("項目經理贊成"+user+"預支差旅費用"+requestNum+"元的請求");
            return true;
        }else{
            //超過5000,繼續傳遞給級別更高的人處理
            if(this.successor!=null){
                return this.successor.handlePreFeeRequest(user, requestNum);
            }
        }
        return false;
    }
}

其它的處理相似,就不去演示了。

(3)準備好了各個處理職責的類,看看客戶端如何調用,示例代碼以下:

public class Client {
    public static void main(String[] args) {
        //先要組裝職責鏈   
        Handler h1 = new GeneralManager();
        Handler h2 = new DepManager();
        Handler h3 = new ProjectManager();
        h3.setSuccessor(h2);
        h2.setSuccessor(h1);
  
        //開始測試申請聚餐費用
        String ret1 = h3.handleFeeRequest("小李", 300);
        System.out.println("the ret1="+ret1);
        String ret2 = h3.handleFeeRequest("小李", 600);
        System.out.println("the ret2="+ret2);
        String ret3 = h3.handleFeeRequest("小李", 1200);
        System.out.println("the ret3="+ret3);
     
        //開始測試申請差旅費用
        h3.handlePreFeeRequest("小張", 3000);
        h3.handlePreFeeRequest("小張", 6000);
        h3.handlePreFeeRequest("小張", 32000);
   }
}

運行的結果以下:

the ret1=項目經理贊成小李聚餐費用300.0元的請求
the ret2=部門經理贊成小李聚餐費用600.0元的請求
the ret3=總經理贊成小李聚餐費用1200.0元的請求
項目經理贊成小張預支差旅費用3000.0元的請求
部門經理贊成小張預支差旅費用6000.0元的請求
總經理贊成小張預支差旅費用32000.0元的請求
  1. 通用請求的處理方式

上面的實現看起來很容易,可是仔細想一想,這樣實現有沒有什麼問題呢?

這種實現方式有一個很明顯的問題,那就是隻要增長一個業務,就須要修改職責的接口,這是很不靈活的,Java開發中很強調面向接口編程,所以接口應該相對保持穩定,接口一改,須要修改的地方就太多了,頻繁修改接口絕對不是個好主意

那有沒有什麼好方法來實現呢?分析一下如今變化的東西:

一是不一樣的業務須要傳遞的業務數據不一樣;

二是不一樣的業務請求的方法不一樣;

三是不一樣的職責對象處理這些不一樣的業務請求的業務邏輯不一樣;

如今有一種簡單的方式,能夠較好的解決這些問題。首先定義一套通用的調用框架,用一個通用的請求對象來封裝請求傳遞的參數;而後定義一個通用的調用方法,這個方法不去區分具體業務,全部的業務都是這一個方法,那麼具體的業務如何區分呢,就是在通用的請求對象裏面會有一個業務的標記;到了職責對象裏面,願意處理就跟原來同樣的處理方式,若是不肯意處理,就傳遞到下一個處理對象就行了

對於返回值也能夠來個通用的,最簡單的就是使用Object類型

看例子吧,爲了示範,先就假定只有一個業務方法,等把這一個方法搞定了,明白了,而後再擴展一個業務方法,就能清晰地看出這種設計的好處了。

(1)先看看通用的請求對象的定義,示例代碼以下:

/**
 * 通用的請求對象
 */
public class RequestModel {
    /**
     * 表示具體的業務類型
     */
    private String type;
    /**
     * 經過構造方法把具體的業務類型傳遞進來
     * @param type 具體的業務類型
     */
    public RequestModel(String type){
        this.type = type;
    }
    public String getType() {
        return type;
    }  
}

(2)看看此時的通用職責處理對象,在這裏要實現一個通用的調用框架,示例代碼以下:

/**
 * 定義職責對象的接口
 */
public abstract class Handler {
    /**
     * 持有下一個處理請求的對象
     */
    protected Handler successor = null;
    /**
     * 設置下一個處理請求的對象
     * @param successor 下一個處理請求的對象
     */
    public void setSuccessor(Handler successor){
        this.successor = successor;
    }
    /**
     * 通用的請求處理方法
     * @param rm 通用的請求對象
     * @return 處理後須要返回的對象
     */
    public Object handleRequest(RequestModel rm){
        if(successor != null){
            //這個是默認的實現,若是子類不肯意處理這個請求,
            //那就傳遞到下一個職責對象去處理
            return this.successor.handleRequest(rm);
        }else{
            System.out.println("沒有後續處理或者暫時不支持這樣的功能處理");
            return false;
        }
    }
}

(3)如今來加上第一個業務,就是「聚餐費用申請」的處理,爲了描述具體的業務數據,須要擴展通用的請求對象,把業務數據封裝進去,另外定義一個請求對象,示例代碼以下:

/**
 * 封裝跟聚餐費用申請業務相關的請求數據
 */
public class FeeRequestModel extends RequestModel{
    /**
     * 約定具體的業務類型
     */
    public final static String FEE_TYPE = "fee";
    public FeeRequestModel() {
        super(FEE_TYPE);
    }
    /**
     * 申請人
     */
    private String user;
    /**
     * 申請金額
     */
    private double fee;
    public String getUser() {
        return user;
    }
    public void setUser(String user) {
        this.user = user;
    }
    public double getFee() {
        return fee;
    }
    public void setFee(double fee) {
        this.fee = fee;
    }
}

(4)接下來該實現職責對象的處理了,大同小異的,仍是看一個就好了,看看項目經理的處理吧,在這個處理類裏面,首先要覆蓋父類的通用業務處理方法,而後在裏面處理本身想要實現的業務,不想處理的就讓父類去處理,父類會默認的傳遞給下一個處理對象,示例代碼以下:

/**
 * 實現項目經理處理聚餐費用申請的對象
 */
public class ProjectManager extends Handler{
    public Object handleRequest(RequestModel rm){
        if(FeeRequestModel.FEE_TYPE.equals(rm.getType())){
            //表示聚餐費用申請
            return handleFeeRequest(rm);
        }else{
            //其它的項目經理暫時不想處理
            return super.handleRequest(rm);
        }
    }
    private Object handleFeeRequest(RequestModel rm) {
        //先把通用的對象造型回來
        FeeRequestModel frm = (FeeRequestModel)rm;
        String str = "";
        //項目經理的權限比較小,只能在500之內
        if(frm.getFee() < 500){
            //爲了測試,簡單點,只贊成小李的
            if("小李".equals(frm.getUser())){
                str = "項目經理贊成"+frm.getUser()+"聚餐費用"+frm.getFee()+"元的請求";
            }else{
                //其它人一概不一樣意
                str = "項目經理不一樣意"+frm.getUser()+"聚餐費用"+frm.getFee()+"元的請求";
            }
            return str;
        }else{
            //超過500,繼續傳遞給級別更高的人處理
            if(this.successor!=null){
                return successor.handleRequest(rm);
            }
        }
        return str;
    }
}

部門經理、總經理的處理對象和項目經理的處理相似,就不去示例了。

(5)客戶端也須要變化,對於客戶端,惟一的麻煩是須要知道每一個業務對應的具體的請求對象,由於要封裝業務數據進去,示例代碼以下:

public class Client {
    public static void main(String[] args) {
        //先要組裝職責鏈   
        Handler h1 = new GeneralManager();
        Handler h2 = new DepManager();
        Handler h3 = new ProjectManager();
        h3.setSuccessor(h2);
        h2.setSuccessor(h1);
     
        //開始測試申請聚餐費用
        FeeRequestModel frm = new FeeRequestModel();
        frm.setFee(300);
        frm.setUser("小李");
        //調用處理
        String ret1 = (String)h3.handleRequest(frm);
        System.out.println("ret1="+ret1);
     
        //從新設置申請金額,再調用處理
        frm.setFee(800);    
        h3.handleRequest(frm);
        String ret2 = (String)h3.handleRequest(frm);
        System.out.println("ret2="+ret2);
     
        //從新設置申請金額,再調用處理
        frm.setFee(1600);   
        h3.handleRequest(frm);
        String ret3 = (String)h3.handleRequest(frm);
        System.out.println("ret3="+ret3);
    }
}

運行結果以下:

ret1=項目經理贊成小李聚餐費用300.0元的請求
ret2=部門經理贊成小李聚餐費用800.0元的請求
ret3=總經理贊成小李聚餐費用1600.0元的請求

(6)接下來看看如何在不改動現有的框架的前提下,擴展新的業務,這樣才能說明這種設計的靈活性。

假如就是要實現上面示例過的另一個功能「預支差旅費申請」吧。要想擴展新的業務,第一步就是新建一個封裝業務數據的對象,示例代碼以下:

/**
 * 封裝跟預支差旅費申請業務相關的請求數據
 */
public class PreFeeRequestModel extends RequestModel{
    /**
     * 約定具體的業務類型
     */
    public final static String FEE_TYPE = "preFee";
    public PreFeeRequestModel() {
        super(FEE_TYPE);
    }
    /**
     * 申請人
     */
    private String user;
    /**
     * 申請金額
     */
    private double fee;
    public String getUser() {
        return user;
    }
    public void setUser(String user) {
        this.user = user;
    }
    public double getFee() {
        return fee;
    }
    public void setFee(double fee) {
        this.fee = fee;
    }
}

有些朋友會發現,這個對象跟封裝聚餐費用申請業務數據的對象幾乎徹底同樣的,這裏要說明一下,同樣的緣由主要是咱們爲了演示簡單,設計得類似,實際業務中多是不同的,所以,最好仍是一個業務一個對象,若是確實有公共的數據,能夠定義公共的父類,最好不要讓不一樣的業務使用統一個對象,容易混淆。

(7)對於具體進行職責處理的類,比較好的方式就是擴展出子類來,而後在子類裏面實現新加入的業務,固然也能夠直接在原來的對象上改。仍是採用擴展出子類的方式吧,仍是看看新的項目經理的處理類,示例代碼以下:

/**
 * 實現爲項目經理增長預支差旅費用申請處理的功能的子對象,
 * 如今的項目經理既能夠處理聚餐費用申請,又能夠處理預支差旅費用申請
 */
public class ProjectManager2 extends ProjectManager{
    public Object handleRequest(RequestModel rm){
        if(PreFeeRequestModel.FEE_TYPE.equals(rm.getType())){
            //表示預支差旅費用申請
            return myHandler(rm);
        }else{
            //其它的讓父類去處理
            return super.handleRequest(rm);
        }
    }
    private Object myHandler(RequestModel rm) {
        //先把通用的對象造型回來
        PreFeeRequestModel frm = (PreFeeRequestModel)rm;
        //項目經理的權限比較小,只能在5000之內
        if(frm.getFee() < 5000){
            //工做須要嘛,通通贊成
            System.out.println("項目經理贊成"+frm.getUser()+"預支差旅費用"+frm.getFee()+"元的請求");
            return true;
        }else{
            //超過5000,繼續傳遞給級別更高的人處理
            if(this.successor!=null){
                return this.successor.handleRequest(rm);
            }
        }
        return false;
    }
}

部門經理和總經理的處理相似,就不去示例了。

(8)看看此時的測試,示例以下:

public class Client {
    public static void main(String[] args) {
        //先要組裝職責鏈   
        Handler h1 = new GeneralManager2();
        Handler h2 = new DepManager2();
        Handler h3 = new ProjectManager2();
        h3.setSuccessor(h2);
        h2.setSuccessor(h1);
     
        //開始測試申請聚餐費用
        FeeRequestModel frm = new FeeRequestModel();
        frm.setFee(300);
        frm.setUser("小李");
        //調用處理
        String ret1 = (String)h3.handleRequest(frm);
        System.out.println("ret1="+ret1);
     
        //從新設置申請金額,再調用處理
        frm.setFee(800);    
        h3.handleRequest(frm);
        String ret2 = (String)h3.handleRequest(frm);
        System.out.println("ret2="+ret2);
     
        //從新設置申請金額,再調用處理
        frm.setFee(1600);   
        h3.handleRequest(frm);
        String ret3 = (String)h3.handleRequest(frm);
        System.out.println("ret3="+ret3);
     
        //開始測試申請預支差旅費用
        PreFeeRequestModel pfrm = new PreFeeRequestModel();
        pfrm.setFee(3000);
        pfrm.setUser("小張");
        //調用處理
        h3.handleRequest(pfrm);
        //從新設置申請金額,再調用處理
        pfrm.setFee(6000);
        h3.handleRequest(pfrm);
        //從新設置申請金額,再調用處理
        pfrm.setFee(36000);
        h3.handleRequest(pfrm);
    }
}

運行一下,試試看,運行結果以下:

ret1=項目經理贊成小李聚餐費用300.0元的請求
ret2=部門經理贊成小李聚餐費用800.0元的請求
ret3=總經理贊成小李聚餐費用1600.0元的請求
項目經理贊成小張預支差旅費用3000.0元的請求
部門經理贊成小張預支差旅費用6000.0元的請求
總經理贊成小張預支差旅費用36000.0元的請求

好好體會一下這種設計方式的好處,至關的通用和靈活,有了新業務,只須要添加實現新功能的對象就能夠了,可是帶來的缺陷就是可能會形成對象層次過多,或者出現較多的細粒度的對象,極端狀況下,每次就擴展一個方法,會出現大量只處理一個功能的細粒度對象

##3.3 功能鏈## 在實際開發中,常常會出現一個把職責鏈稍稍變形的用法。在標準的職責鏈中,一個請求在職責鏈中傳遞,只要有一個對象處理了這個請求,就會中止

如今稍稍變一下,改爲一個請求在職責鏈中傳遞,每一個職責對象負責處理請求的某一方面的功能,處理完成後,不是中止,而是繼續向下傳遞請求,當請求經過不少職責對象處理事後,功能也就處理完了,把這樣的職責鏈稱爲功能鏈

考慮這樣一個功能,在實際應用開發中,在進行業務處理以前,一般須要進行權限檢查、通用數據校驗、數據邏輯校驗等處理,而後纔開始真正的業務邏輯實現。能夠把這些功能分散到一個功能鏈中,這樣作的目的是使程序結構更加靈活,並且複用性會更好,好比通用的權限檢查就只須要作一份,而後就能夠在多個功能鏈中使用了。

有些朋友看到這裏,可能會想,這不是可使用裝飾模式來實現嗎?沒錯,可使用裝飾模式來實現這樣的功能,可是職責鏈會更靈活一些,由於裝飾模式是在已有的功能上增長新的功能,多個裝飾器之間會有必定的聯繫而職責鏈模式的各個職責對象實現的功能,相互之間是沒有關聯的,是本身實現屬於本身處理的那一份功能

可能有些朋友會想到這很相似於在Web應用開發中的過濾器Filter,沒錯,過濾器鏈就相似於一個功能鏈,每一個過濾器負責本身的處理,而後轉交給下一個過濾器,直到把全部的過濾器都走完,而後進入到Servlet裏面進行處理。最多見的過濾器功能,好比權限檢查、字符集轉換等,基本上都是Web應用的標配。

接下來在示例中,實現這樣的功能:實現商品銷售的業務處理,在真正進行銷售的業務處理以前,須要對傳入處理的數據,進行權限檢查、通用數據檢查和數據邏輯檢查,只有這些檢查都能經過的狀況下,才說明傳入的數據是正確的、有效的數據,才能夠進行真正的業務功能處理。

  1. 首先定義已有的業務功能和封裝業務數據的對象,用前面出現過的那個保存銷售信息的業務,爲了簡單,就不去定義接口了,示例代碼以下:
/**
 * 商品銷售管理模塊的業務處理
 */
public class GoodsSaleEbo {
    /**
     * 保存銷售信息,原本銷售數據應該是多條,太麻煩了,爲了演示,簡單點
     * @param user 操做人員
     * @param customer 客戶
     * @param saleModel 銷售數據
     * @return 是否保存成功
     */
    public boolean sale(String user,String customer,SaleModel saleModel){
        //若是所有在這裏處理,基本的順序是
        //1:權限檢查
        //2:通用數據檢查(這個也可能在表現層已經做過了)
        //3:數據邏輯校驗
     
        //4:真正的業務處理
     
        //可是如今經過功能鏈來作,這裏就主要負責構建鏈
        //暫時尚未功能鏈,等實現好了各個處理對象再回來添加
        return true;
    }
}

對應的封裝銷售數據的對象,示例代碼以下:

/**
 * 封裝銷售單的數據,簡單的示意一下
 */
public class SaleModel {
    /**
     * 銷售的商品
     */
    private String goods;
    /**
     * 銷售的數量
     */
    private int saleNum;
    public String getGoods() {
        return goods;
    }
    public void setGoods(String goods) {
        this.goods = goods;
    }
    public int getSaleNum() {
        return saleNum;
    }
    public void setSaleNum(int saleNum) {
        this.saleNum = saleNum;
    }
    public String toString(){
        return "商品名稱="+goods+",銷售數量="+saleNum;
    }
}
  1. 定義一個用來處理保存銷售數據功能的職責對象的接口,示例代碼以下:
/**
 * 定義職責對象的接口
 */
public abstract class SaleHandler {
    /**
     * 持有下一個處理請求的對象
     */
    protected SaleHandler successor = null;
    /**
     * 設置下一個處理請求的對象
     * @param successor 下一個處理請求的對象
     */
    public void setSuccessor(SaleHandler successor){
        this.successor = successor;
    }
    /**
     * 處理保存銷售信息的請求
     * @param user 操做人員
     * @param customer 客戶
     * @param saleModel 銷售數據
     * @return 是否處理成功
     */
    public abstract boolean sale(String user,String customer,SaleModel saleModel);
}
  1. 實現各個職責處理對象,每一個職責對象負責請求的一個方面的處理,把這些職責對象都走完了,功能也就實現完了。先定義處理安全檢查的職責對象,示例代碼以下:
/**
 * 進行權限檢查的職責對象
 */
public class SaleSecurityCheck extends SaleHandler{
    public boolean sale(String user, String customer, SaleModel saleModel) {
        //進行權限檢查,簡單點,就小李能經過
        if("小李".equals(user)){
            return this.successor.sale(user, customer, saleModel);
        }else{
            System.out.println("對不起"+user+",你沒有保存銷售信息的權限");
            return false;
        }     
    }
}

接下來定義通用數據檢查的職責對象,示例代碼以下:

/**
 * 進行數據通用檢查的職責對象
 */
public class SaleDataCheck extends SaleHandler{
    public boolean sale(String user, String customer, SaleModel saleModel) {
        //進行數據通用檢查,稍麻煩點,每一個數據都要檢測
        if(user==null || user.trim().length()==0){
            System.out.println("申請人不能爲空");
            return false;
        }
        if(customer==null || customer.trim().length()==0){
            System.out.println("客戶不能爲空");
            return false;
        }
        if(saleModel==null ){
            System.out.println("銷售商品的數據不能爲空");
            return false;
        }
        if(saleModel.getGoods() == null || saleModel.getGoods().trim().length()==0){
            System.out.println("銷售的商品不能爲空");
            return false;
        }
        if(saleModel.getSaleNum()==0){
            System.out.println("銷售商品的數量不能爲0");
            return false;
        }     
        //若是經過了上面的檢測,那就向下繼續執行
        return this.successor.sale(user, customer, saleModel);
     }
}

再看看進行數據邏輯檢查的職責對象,示例代碼以下:

/**
 * 進行數據邏輯檢查的職責對象
 */
public class SaleLogicCheck extends SaleHandler{
    public boolean sale(String user, String customer, SaleModel saleModel) {
        //進行數據的邏輯檢查,好比檢查ID的惟一性,主外鍵的對應關係等等
        //這裏應該檢查這種主外鍵的對應關係,好比銷售商品是否存在
        //爲了演示簡單,直接經過吧
     
        //若是經過了上面的檢測,那就向下繼續執行
        return this.successor.sale(user, customer, saleModel);
    }
}

最後是真正的業務處理的職責對象,示例代碼以下:

/**
 * 真正處理銷售的業務功能的職責對象
 */
public class SaleMgr extends SaleHandler{
    public boolean sale(String user, String customer, SaleModel saleModel) {
        //進行真正的業務邏輯處理
        System.out.println(user+"保存了"+customer+"購買 "+saleModel+" 的銷售數據");
        return true;
    }
}
  1. 實現好了各個職責對象處理,回過頭來看看如何具體實現業務處理,在業務對象裏面進行功能鏈的組合,示例代碼以下:
public class GoodsSaleEbo {
    /**
     * 保存銷售信息,原本銷售數據應該是多條,太麻煩了,爲了演示,簡單點
     * @param user 操做人員
     * @param customer 客戶
     * @param saleModel 銷售數據
     * @return 是否保存成功
     */
    public boolean sale(String user,String customer,SaleModel saleModel){
        //若是所有在這裏處理,基本的順序是
        //1:權限檢查
        //2:通用數據檢查(這個也可能在表現層已經做過了)
        //3:數據邏輯校驗
     
        //4:真正的業務處理
     
        //可是如今經過功能鏈來作,這裏就主要負責構建鏈
        SaleSecurityCheck ssc = new SaleSecurityCheck();
        SaleDataCheck sdc = new SaleDataCheck();
        SaleLogicCheck slc = new SaleLogicCheck();
        SaleMgr sd = new SaleMgr();
        ssc.setSuccessor(sdc);
        sdc.setSuccessor(slc);
        slc.setSuccessor(sd);
        //向鏈上的第一個對象發出處理的請求
        return ssc.sale(user, customer, saleModel);
    }
}
  1. 寫個客戶端,調用業務對象,測試一下看看,示例代碼以下:
public class Client {
    public static void main(String[] args) {
        //建立業務對象
        GoodsSaleEbo ebo = new GoodsSaleEbo();
        //準備測試數據
        SaleModel saleModel = new SaleModel();
        saleModel.setGoods("張學友懷舊經典");
        saleModel.setSaleNum(10);
     
        //調用業務功能
        ebo.sale("小李", "張三", saleModel);
        ebo.sale("小張", "李四", saleModel);
    }
}

運行一下,試試看,運行結果以下:

小李保存了張三購買 商品名稱=張學友懷舊經典,銷售數量=10 的銷售數據
對不起小張,你沒有保存銷售信息的權限

##3.4 職責鏈模式的優缺點##

  1. 請求者和接收者鬆散耦合

在職責鏈模式裏面,請求者並不知道接收者是誰,也不知道具體如何處理,請求者只是負責向職責鏈發出請求就能夠了。而每一個職責對象也不用管請求者或者是其它的職責對象,只負責處理本身的部分,其它的就交由其它的職責對象去處理。也就是說,請求者和接收者是徹底解耦的。

  1. 動態組合職責

職責鏈模式會把功能處理分散到單獨的職責對象裏面,而後在使用的時候,能夠動態組合職責造成職責鏈,從而能夠靈活的給對象分配職責,也能夠靈活的實現和改變對象的職責。

  1. 產生不少細粒度對象

職責鏈模式會把功能處理分散到單獨的職責對象裏面,也就是每一個職責對象只是處理一個方面的功能,要把整個業務處理完,須要大量的職責對象的組合,這會產生大量的細粒度職責對象。

  1. 不必定能被處理

職責鏈模式的每一個職責對象只負責本身處理的那一部分,所以可能會出現某個請求,把整個鏈傳遞完了,都沒有職責對象處理它。這就須要在使用職責鏈模式的時候注意,須要提供默認的處理,而且注意構建的鏈的有效性

##3.5 思考職責鏈模式##

  1. 職責鏈模式的本質

職責鏈模式的本質:分離職責,動態組合。

分離職責是前提,只有先把複雜功能分開,拆分紅不少的步驟和小的功能處理,而後才能合理規劃和定義職責類,能夠有不少的職責類來負責處理某一個功能,讓每一個職責類負責處理功能的某一個方面,在運行期間進行動態組合,造成一個處理的鏈,把這個鏈運行完,那麼功能也就處理完了。

動態組合纔是職責鏈模式的精華所在,由於要實現請求對象和處理對象的解耦,請求對象不知道誰纔是真正的處理對象,所以要動態的把可能的處理對象組合起來,因爲組合的方式是動態的,這就意味着能夠很方便的修改和添加新的處理對象,從而讓系統更加靈活和具備更好的擴展性。

固然這麼作還會有一個潛在的優勢,就是能夠加強職責功能的複用性。若是職責功能是不少地方均可以使用的公共功能,那麼它能夠應用在多個職責鏈中複用。

  1. 什麼時候選用職責鏈模式

建議在以下狀況中,選用職責鏈模式:

若是有多個對象能夠處理同一個請求,可是具體由哪一個對象來處理該請求,是運行時刻動態肯定的。這種狀況可使用職責鏈模式,把處理請求的對象實現成爲職責對象,而後把它們構成一個職責鏈,當請求在這個鏈中傳遞的時候,具體由哪一個職責對象來處理,會在運行時動態判斷。

若是你想在不明確指定接收者的狀況下,向多個對象中的一個提交一個請求的話,可使用職責鏈模式,職責鏈模式實現了請求者和接收者之間的解耦,請求者不須要知道到底是哪個接收者對象來處理了請求。

若是想要動態指定處理一個請求的對象集合,可使用職責鏈模式,職責鏈模式能動態的構建職責鏈,也就是動態的來決定到底哪些職責對象來參與處處理請求中來,至關因而動態指定了處理一個請求的職責對象集合。

##3.6 相關模式##

  1. 職責鏈模式和組合模式

這兩個模式能夠組合使用。

能夠把職責對象經過組合模式來組合,這樣能夠經過組合對象自動遞歸的向上調用,由父組件做爲子組件的後繼,從而造成鏈。

這也就是前面提到過的使用外部已有的連接,這種狀況在客戶端使用的時候,就不用再構造鏈了,雖然不構造鏈,可是須要構造組合對象樹,是同樣的。

  1. 職責鏈模式和裝飾模式

這兩個模式類似,從某個角度講,能夠相互模擬實現對方的功能

裝飾模式可以動態的給被裝飾對象添加功能,要求裝飾器對象和被裝飾的對象實現相同的接口。而職責鏈模式能夠實現動態的職責組合,標準的功能是有一個對象處理就結束,可是若是處理完本職責不急於結束,而是繼續向下傳遞請求,那麼功能就和裝飾模式的功能差很少了,每一個職責對象就相似於裝飾器,能夠實現某種功能。

並且兩個模式的本質也相似,都須要在運行期間動態組合,裝飾模式是動態組合裝飾器,而職責鏈是動態組合處理請求的職責對象的鏈

可是從標準的設計模式上來說,這兩個模式仍是有較大區別的,這點要注意。首先是目的不一樣,裝飾模式是要實現透明的爲對象添加功能,而職責鏈模式是要實現發送者和接收者解耦;另一個,裝飾模式是無限遞歸調用的,能夠有任意多個對象來裝飾功能,可是職責鏈模式是有一個處理就結束

  1. 職責鏈模式和策略模式

這兩個模式能夠組合使用。

這兩個模式有類似之處,若是把職責鏈簡化到直接就能選擇到相應的處理對象,那就跟策略模式的選擇差很少,所以能夠用職責鏈來模擬策略模式的功能。只是若是把職責鏈簡化到這個地步,也就不存在鏈了,也就稱不上是職責鏈了。

兩個模式能夠組合使用,能夠在職責鏈模式的某個職責的實現的時候,使用策略模式來選擇具體的實現,一樣也能夠在策略模式的某個策略實現裏面,使用職責鏈模式來實現功能處理

同理職責鏈模式也能夠和狀態模式組合使用。

相關文章
相關標籤/搜索