用來解決上述問題的一個合理的解決方案,就是使用職責鏈模式。那麼什麼是職責鏈模式呢?
(1)職責鏈模式定義
(2)應用職責鏈模式來解決的思路
仔細分析上面的場景,當客戶端提出一個聚餐費用的申請,後續處理這個申請的對象,項目經理、部門經理和總經理,自然的形成了一個鏈,從項目經理à部門經理à總經理,客戶端的申請請求就在這個鏈中傳遞,直到有領導處理爲止。看起來,上面的功能要求很適合採用職責鏈來處理這個業務。
要想讓處理請求的流程可以靈活的變動,一個基本的思路,那就是動態構建流程步驟,這樣隨時都可以重新組合出新的流程來。而要讓處理請求的對象也要很靈活,那就要讓它足夠簡單,最好是隻實現單一的功能,或者是有限的功能,這樣更有利於修改和複用。
職責鏈模式就很好的體現了上述的基本思路,首先職責鏈模式會定義一個所有處理請求的對象都要繼承實現的抽象類,這樣就有利於隨時切換新的實現;其次每個處理請求對象只實現業務流程中的一步業務處理,這樣使其變得簡單;最後職責鏈模式會動態的來組合這些處理請求的對象,把它們按照流程動態組合起來,並要求它們依次調用,這樣就動態的實現了流程。
這樣一來,如果流程發生了變化,只要重新組合就好了;如果某個處理的業務功能發生了變化,一個方案是修改該處理對應的處理對象,另一個方案是直接提供一個新的實現,然後在組合流程的時候,用新的實現替換掉舊的實現就可以了。
23.2.2 模式結構和說明
職責鏈模式的結構如圖23.1所示:
圖23.1 職責鏈模式結構圖
Handler:
定義職責的接口,通常在這裏定義處理請求的方法,可以在這裏實現後繼鏈。
ConcreteHandler:
實現職責的類,在這個類裏面,實現對在它職責範圍內請求的處理,如果不處理,就繼續轉發請求給後繼者。
Client:
職責鏈的客戶端,向鏈上的具體處理者對象提交請求,讓職責鏈負責處理。
23.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(); } } |
23.2.4 使用職責鏈模式重寫示例
要使用職責鏈模式來重寫示例,還是先來實現如下的功能:當某人提出聚餐費用申請的請求後,該請求會在項目經理à部門經理à總經理這樣一條領導處理鏈上進行傳遞,發出請求的人並不知道誰會來處理他的請求,每個領導會根據自己的職責範圍,來判斷是處理請求還是把請求交給更高級的領導,只要有領導處理了,傳遞就結束了。
需要把每位領導的處理獨立出來,實現成單獨的職責處理對象,然後爲它們提供一個公共的、抽象的父職責對象,這樣就可以在客戶端來動態的組合職責鏈,實現不同的功能要求了。還是看一下示例的整體結構,會有助於對示例的理解和把握。如圖23.2所示:
圖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); } |
(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元的請求 |
看起來結果跟前面不用模式的實現方案的運行結果是一樣的,它們本來就是實現的同樣的功能,只不過實現方式不同而已。
(4)如何運行的
理解了示例的整體結構和具體實現,那麼示例的具體運行過程是怎樣的呢?
下面就以“小李申請聚餐費用1200元”這個費用申請爲例來說明,調用過程的示意圖如圖23.3所示:
圖23.3 職責鏈示例調用過程示意圖