職責鏈

1.場景問題

1.1  申請聚餐費用

來考慮這樣一個功能:申請聚餐費用的管理。html

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

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

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

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

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

1.2  不用模式的解決方案

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

/**
 * 處理聚餐費用申請的對象
 */
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;
    }
}

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

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);
    }
}

運行結果以下:this

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  有何問題

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

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

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

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

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

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

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

2  解決方案

2.1  職責鏈模式來解決

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

(1)職責鏈模式定義

wKioL1lkh5XjKNpwAAB4a4z1y0I487.png

(2)應用職責鏈模式來解決的思路

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

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

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

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

2.2  模式結構和說明

wKioL1lkiUrD64JLAABq2xHDQ5c214.png

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();
}

(2)接下來看看具體的職責實現對象,示例代碼以下:

/**
 * 具體的職責對象,用來處理請求
 */
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的示意代碼幾乎是同樣的,所以就不去贅述了

(3)接下來看看客戶端的示意,示例代碼以下:

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

2.4  使用職責鏈模式重寫示例

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

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

wKiom1lkifzA4UTYAADf5IWzjrc426.png

(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);
}

(2)實現各自的職責

       如今實現的處理聚餐費用流程是:申請人提出的申請交給項目經理處理,項目經理的處理權限是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;
    }
}

(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("小張", 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元的請求

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

3.1功能鏈

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

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

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

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

可能有些朋友會想到這很相似於在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;
    }
}

(2)定義一個用來處理保存銷售數據功能的職責對象的接口,示例代碼以下:

/**
 * 定義職責對象的接口
 */
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);
}

(3)實現各個職責處理對象,每一個職責對象負責請求的一個方面的處理,把這些職責對象都走完了,功能也就實現完了。先定義處理安全檢查的職責對象,示例代碼以下:

/**
 * 進行權限檢查的職責對象
 */
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;
    }
}

(4)實現好了各個職責對象處理,回過頭來看看如何具體實現業務處理,在業務對象裏面進行功能鏈的組合,示例代碼以下:

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);
    }
}

(5)寫個客戶端,調用業務對象,測試一下看看,示例代碼以下:

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.2 Struts2攔截器實現

Main.java

public class Main {
    public static void main(String[] args) {
        new ActionInvocation().invoke();
    }
}

ActionInvocation.java

import java.util.ArrayList;
import java.util.List;

public class ActionInvocation {
    List<Interceptor> interceptors = new ArrayList<Interceptor>();
    int index = -1; // 來指示循環到了第幾個interceptor了
    Action a = new Action();

    public ActionInvocation() // 構造函數
    {
        this.interceptors.add(new FirstInterceptor());
        this.interceptors.add(new SecondInterceptor());
    }

    public void invoke() {
        index++;
        if (index >= this.interceptors.size()) {
            a.execute();
        } else {

            this.interceptors.get(index).intercept(this);
        }
    }
}

Interceptor.java

public interface Interceptor {
    public void intercept(ActionInvocation invocation);
}

FirstInterceptor.java

public class FirstInterceptor implements Interceptor {

    public void intercept(ActionInvocation invocation) {
        System.out.println("第一個攔截器開始");
        invocation.invoke();
        System.out.println("第一個攔截器結束");
    }

}

SecondInterceptor.java

public class SecondInterceptor implements Interceptor {

    public void intercept(ActionInvocation invocation) {
        System.out.println("第二個攔截器開始");
        invocation.invoke();
        System.out.println("第二個攔截器結束");
    }

}

Action.java

public class Action {
    public void execute() {
        System.out.println("執行execute方法!");
    }
}

看下運行結果:

第一個攔截器開始
第二個攔截器開始
執行execute方法!
第二個攔截器結束
第一個攔截器結束

3.3 Filter過濾器實例

      在處理用戶的請求時可能要根據不一樣的狀況對請求添加不一樣的處理邏輯,在這時候就能夠利用責任鏈進行設計。當須要添加一個處理邏輯時能夠很方便的添加一個處理的節點。如今咱們的需求是處理用戶的請求,將用戶提交的字符串信息進行層層處理,同時在處理完成以後返回結果時,也要對返回的字符串進行層層處理,而處理返回的狀況時其處理的順序和先前是正好相反的順序。
      在FilterChain中繼承了Filter接口,從而實現了doFilter方法,在FilterChain中又有一個index變量,該變量是用來標記當前訪問的是哪個過濾器,這些過濾器是存放在ArrayList中的,這樣用戶在使用的時候就能夠實現本身的過濾器,編寫本身的處理邏輯,從而將本身的過濾器添加到ArrayList中,再調用FilterChain的doFilter方法遍歷整個責任鏈。
1)首先創建用戶的請求和接收對象Request和Response:

public class Request {    
    String requestStr;    
    
    public String getRequestStr() {    
        return requestStr;    
    }    
    
    public void setRequestStr(String requestStr) {    
        this.requestStr = requestStr;    
    }    
}
public class Response {    
    String responseStr;    
    
    public String getResponseStr() {    
        return responseStr;    
    }    
    
    public void setResponseStr(String responseStr) {    
        this.responseStr = responseStr;    
    }    
}

咱們將處理用戶信息的邏輯抽象成爲一個個的過濾器,進一步抽象出過濾器接口Filter:

public interface Filter {    
    public void doFilter(Request request, Response response, FilterChain chain);    
    
}

注意在Filte接口中doFilter方法參數中有FilterChain的一個變量,咱們再創建FilterChain類:

import java.util.ArrayList;    
import java.util.List;    
public class FilterChain implements Filter {    
    List<Filter> filters = new ArrayList<Filter>();    
    int index = 0;    
    
    public FilterChain addFilter(Filter f) {    
        this.filters.add(f);    
        return this;    
    }  
    @Override    
    public void doFilter(Request request, Response response, FilterChain chain) {    
        if (index == filters.size())    
            return;    
        Filter f = filters.get(index);    
        index++;    
        f.doFilter(request, response, chain);    
    }    
}

下面咱們編寫三個過濾器:

public class HTMLFilter implements Filter {    
    @Override    
    public void doFilter(Request request, Response response,FilterChain chain) {    
        request.requestStr = request.getRequestStr().replace("<", "[")    
                .replace(">", "] --------HTMLFilter");    
        chain.doFilter(request, response, chain);    
        response.responseStr += "--------HTMLFilter";    
    }    
}
public class SesitiveFilter implements Filter {    
    @Override    
    public void doFilter(Request request, Response response, FilterChain chain) {    
        request.requestStr = request.getRequestStr().replace("敏感", "  ")    
                .replace("貓貓", "haha------SesitiveFilter");    
        chain.doFilter(request, response, chain);    
        response.responseStr += "------SesitiveFilter";    
    }    
}
public class FaceFilter implements Filter {    
    @Override    
    public void doFilter(Request request, Response response, FilterChain chain) {    
        request.requestStr = request.getRequestStr().replace(":)",    
                "^V^-------FaceFilter");    
        chain.doFilter(request, response, chain);    
        response.responseStr += "-------FaceFilter";    
    }    
}

測試:

public class Client {  
    public static void main(String[] args) {  
        String message = "敏感詞彙,重慶,<script> 躲貓貓 :)";  
        Request request = new Request();  
        request.setRequestStr(message);  
        Response response = new Response();  
        response.setResponseStr("response");  
        FilterChain fc = new FilterChain();  
        fc.addFilter(new HTMLFilter()).addFilter(new SesitiveFilter());  
        fc.doFilter(request, response, fc);  
        System.out.println("request = " + request.getRequestStr());  
        System.out.println("response = " + response.getResponseStr());  
        System.out.println("=====================================");  
        FilterChain fc2 = new FilterChain();  
        fc2.addFilter(new FaceFilter());  
        fc.addFilter(fc2);  
        fc.doFilter(request, response, fc);  
        System.out.println("request = " + request.getRequestStr());  
        System.out.println("response = " + response.getResponseStr());  
    }  
}

運行結果:

request =   詞彙,重慶,[script] --------HTMLFilter 躲haha------SesitiveFilter :)  
response = response------SesitiveFilter--------HTMLFilter  
=====================================  
request =   詞彙,重慶,[script] --------HTMLFilter 躲haha------SesitiveFilter ^V^-------FaceFilter  
response = response------SesitiveFilter--------HTMLFilter-------FaceFilter

3.3  職責鏈模式的優缺點

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

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

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

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

3.4  相關模式

 職責鏈模式和組合模式
    這兩個模式能夠組合使用。
    能夠把職責對象經過組合模式來組合,這樣能夠經過組合對象自動遞歸的向上調用,由父組件做爲子組件的後繼,從而造成鏈。
    這也就是前面提到過的使用外部已有的連接,這種狀況在客戶端使用的時候,就不用再構造鏈了,雖然不構造鏈,可是須要構造組合對象樹,是同樣的。

 職責鏈模式和裝飾模式
    這兩個模式類似,從某個角度講,能夠相互模擬實現對方的功能。
    裝飾模式可以動態的給被裝飾對象添加功能,要求裝飾器對象和被裝飾的對象實現相同的接口。而職責鏈模式能夠實現動態的職責組合,標準的功能是有一個對象處理就結束,可是若是處理完本職責不急於結束,而是繼續向下傳遞請求,那麼功能就和裝飾模式的功能差很少了,每一個職責對象就相似於裝飾器,能夠實現某種功能。
    並且兩個模式的本質也相似,都須要在運行期間動態組合,裝飾模式是動態組合裝飾器,而職責鏈是動態組合處理請求的職責對象的鏈。
    可是從標準的設計模式上來說,這兩個模式仍是有較大區別的,這點要注意。首先是目的不一樣,裝飾模式是要實現透明的爲對象添加功能,而職責鏈模式是要實現發送者和接收者解耦;另一個,裝飾模式是無限遞歸調用的,能夠有任意多個對象來裝飾功能,可是職責鏈模式是有一個處理就結束。

職責鏈模式和策略模式
    這兩個模式能夠組合使用。
    這兩個模式有類似之處,若是把職責鏈簡化到直接就能選擇到相應的處理對象,那就跟策略模式的選擇差很少,所以能夠用職責鏈來模擬策略模式的功能。只是若是把職責鏈簡化到這個地步,也就不存在鏈了,也就稱不上是職責鏈了。
    兩個模式能夠組合使用,能夠在職責鏈模式的某個職責的實現的時候,使用策略模式來選擇具體的實現,一樣也能夠在策略模式的某個策略實現裏面,使用職責鏈模式來實現功能處理。
    同理職責鏈模式也能夠和狀態模式組合使用。


轉載至:http://sishuok.com/forum/blogPost/list/0/5815.html

   cc老師的設計模式是我目前看過最詳細最有實踐的教程。

相關文章
相關標籤/搜索