在閻宏博士的《JAVA與模式》一書中開頭是這樣描述責任鏈(Chain of Responsibility)模式的:
責任鏈模式是一種對象的行爲模式。在責任鏈模式裏,很多對象由每一個對象對其下家的引用而連接起來形成一條鏈。請求在這個鏈上傳遞,直到鏈上的某一個對象決定處理此請求。發出這個請求的客戶端並不知道鏈上的哪一個對象最終處理這個請求,這使得系統可以在不影響客戶端的情況下動態地重新組織和分配責任。
--------------------------------------------------------------------------------
從擊鼓傳花談起
擊鼓傳花是一種熱鬧而又緊張的飲酒遊戲。在酒宴上賓客依次坐定位置,由一人擊鼓,擊鼓的地方與傳花的地方是分開的,以示公正。開始擊鼓時,花束就開始依次傳遞,鼓聲一落,如果花束在某人手中,則該人就得飲酒。
比如說,賈母、賈赦、賈政、賈寶玉和賈環是五個參加擊鼓傳花遊戲的傳花者,他們組成一個環鏈。擊鼓者將花傳給賈母,開始傳花遊戲。花由賈母傳給賈赦,由賈赦傳給賈政,由賈政傳給賈寶玉,又賈寶玉傳給賈環,由賈環傳回給賈母,如此往復,如下圖所示。當鼓聲停止時,手中有花的人就得執行酒令。
擊鼓傳花便是責任鏈模式的應用。責任鏈可能是一條直線、一個環鏈或者一個樹結構的一部分。
責任鏈模式的結構
下面使用了一個責任鏈模式的最簡單的實現。
責任鏈模式涉及到的角色如下所示:
● 抽象處理者(Handler)角色:定義出一個處理請求的接口。如果需要,接口可以定義 出一個方法以設定和返回對下家的引用。這個角色通常由一個Java抽象類或者Java接口實現。上圖中Handler類的聚合關係給出了具體子類對下家的引用,抽象方法handleRequest()規範了子類處理請求的操作。
● 具體處理者(ConcreteHandler)角色:具體處理者接到請求後,可以選擇將請求處理掉,或者將請求傳給下家。由於具體處理者持有對下家的引用,因此,如果需要,具體處理者可以訪問下家。
源代碼
抽象處理者角色
public abstract class Handler {
/**
* 持有後繼的責任對象
*/
protected Handler successor;
/**
* 示意處理請求的方法,雖然這個示意方法是沒有傳入參數的
* 但實際是可以傳入參數的,根據具體需要來選擇是否傳遞參數
*/
public abstract void handleRequest();
/**
* 取值方法
*/
public Handler getSuccessor() {
return successor;
}
/**
* 賦值方法,設置後繼的責任對象
*/
public void setSuccessor(Handler successor) {
this.successor = successor;
}
}
具體處理者角色
public class ConcreteHandler extends Handler {
/**
* 處理方法,調用此方法處理請求
*/
@Override
public void handleRequest() {
/**
* 判斷是否有後繼的責任對象
* 如果有,就轉發請求給後繼的責任對象
* 如果沒有,則處理請求
*/
if(getSuccessor() != null)
{
System.out.println("放過請求");
getSuccessor().handleRequest();
}else
{
System.out.println("處理請求");
}
}
}
客戶端類
public class Client {
public static void main(String[] args) {
//組裝責任鏈
Handler handler1 = new ConcreteHandler();
Handler handler2 = new ConcreteHandler();
handler1.setSuccessor(handler2);
//提交請求
handler1.handleRequest();
}
}
可以看出,客戶端創建了兩個處理者對象,並指定第一個處理者對象的下家是第二個處理者對象,而第二個處理者對象沒有下家。然後客戶端將請求傳遞給第一個處理者對象。
由於本示例的傳遞邏輯非常簡單:只要有下家,就傳給下家處理;如果沒有下家,就自行處理。因此,第一個處理者對象接到請求後,會將請求傳遞給第二個處理者對象。由於第二個處理者對象沒有下家,於是自行處理請求。活動時序圖如下所示。
使用場景
來考慮這樣一個功能:申請聚餐費用的管理。
很多公司都是這樣的福利,就是項目組或者是部門可以向公司申請一些聚餐費用,用於組織項目組成員或者是部門成員進行聚餐活動。
申請聚餐費用的大致流程一般是:由申請人先填寫申請單,然後交給領導審批,如果申請批准下來,領導會通知申請人審批通過,然後申請人去財務領取費用,如果沒有批准下來,領導會通知申請人審批未通過,此事也就此作罷。
不同級別的領導,對於審批的額度是不一樣的,比如,項目經理只能審批500元以內的申請;部門經理能審批1000元以內的申請;而總經理可以審覈任意額度的申請。
也就是說,當某人提出聚餐費用申請的請求後,該請求會經由項目經理、部門經理、總經理之中的某一位領導來進行相應的處理,但是提出申請的人並不知道最終會由誰來處理他的請求,一般申請人是把自己的申請提交給項目經理,或許最後是由總經理來處理他的請求。
可以使用責任鏈模式來實現上述功能:當某人提出聚餐費用申請的請求後,該請求會在 項目經理—〉部門經理—〉總經理 這樣一條領導處理鏈上進行傳遞,發出請求的人並不知道誰會來處理他的請求,每個領導會根據自己的職責範圍,來判斷是處理請求還是把請求交給更高級別的領導,只要有領導處理了,傳遞就結束了。
需要把每位領導的處理獨立出來,實現成單獨的職責處理對象,然後爲它們提供一個公共的、抽象的父職責對象,這樣就可以在客戶端來動態地組合職責鏈,實現不同的功能要求了。
源代碼
抽象處理者角色類
public abstract class Handler {
/**
* 持有下一個處理請求的對象
*/
protected Handler successor = null;
/**
* 取值方法
*/
public Handler getSuccessor() {
return successor;
}
/**
* 設置下一個處理請求的對象
*/
public void setSuccessor(Handler successor) {
this.successor = successor;
}
/**
* 處理聚餐費用的申請
* @param user 申請人
* @param fee 申請的錢數
* @return 成功或失敗的具體通知
*/
public abstract String handleFeeRequest(String user , double fee);
}
具體處理者角色
public class ProjectManager extends Handler {
@Override
public String handleFeeRequest(String user, double fee) {
String str = "";
//項目經理權限比較小,只能在500以內
if(fee < 500)
{
//爲了測試,簡單點,只同意張三的請求
if("張三".equals(user))
{
str = "成功:項目經理同意【" + user + "】的聚餐費用,金額爲" + fee + "元";
}else
{
//其他人一律不同意
str = "失敗:項目經理不同意【" + user + "】的聚餐費用,金額爲" + fee + "元";
}
}else
{
//超過500,繼續傳遞給級別更高的人處理
if(getSuccessor() != null)
{
return getSuccessor().handleFeeRequest(user, fee);
}
}
return str;
}
}
public class DeptManager extends Handler {
@Override
public String handleFeeRequest(String user, double fee) {
String str = "";
//部門經理的權限只能在1000以內
if(fee < 1000)
{
//爲了測試,簡單點,只同意張三的請求
if("張三".equals(user))
{
str = "成功:部門經理同意【" + user + "】的聚餐費用,金額爲" + fee + "元";
}else
{
//其他人一律不同意
str = "失敗:部門經理不同意【" + user + "】的聚餐費用,金額爲" + fee + "元";
}
}else
{
//超過1000,繼續傳遞給級別更高的人處理
if(getSuccessor() != null)
{
return getSuccessor().handleFeeRequest(user, fee);
}
}
return str;
}
}
public class GeneralManager extends Handler {
@Override
public String handleFeeRequest(String user, double fee) {
String str = "";
//總經理的權限很大,只要請求到了這裏,他都可以處理
if(fee >= 1000)
{
//爲了測試,簡單點,只同意張三的請求
if("張三".equals(user))
{
str = "成功:總經理同意【" + user + "】的聚餐費用,金額爲" + fee + "元";
}else
{
//其他人一律不同意
str = "失敗:總經理不同意【" + user + "】的聚餐費用,金額爲" + fee + "元";
}
}else
{
//如果還有後繼的處理對象,繼續傳遞
if(getSuccessor() != null)
{
return getSuccessor().handleFeeRequest(user, fee);
}
}
return str;
}
}
客戶端類
public class Client {
public static void main(String[] args) {
//先要組裝責任鏈
Handler h1 = new GeneralManager();
Handler h2 = new DeptManager();
Handler h3 = new ProjectManager();
h3.setSuccessor(h2);
h2.setSuccessor(h1);
//開始測試
String test1 = h3.handleFeeRequest("張三", 300);
System.out.println("test1 = " + test1);
String test2 = h3.handleFeeRequest("李四", 300);
System.out.println("test2 = " + test2);
System.out.println("---------------------------------------");
String test3 = h3.handleFeeRequest("張三", 700);
System.out.println("test3 = " + test3);
String test4 = h3.handleFeeRequest("李四", 700);
System.out.println("test4 = " + test4);
System.out.println("---------------------------------------");
String test5 = h3.handleFeeRequest("張三", 1500);
System.out.println("test5 = " + test5);
String test6 = h3.handleFeeRequest("李四", 1500);
System.out.println("test6 = " + test6);
}
}
運行結果如下所示:
純的與不純的責任鏈模式
一個純的責任鏈模式要求一個具體的處理者對象只能在兩個行爲中選擇一個:一是承擔責任,而是把責任推給下家。不允許出現某一個具體處理者對象在承擔了一部分責任後又 把責任向下傳的情況。
在一個純的責任鏈模式裏面,一個請求必須被某一個處理者對象所接收;在一個不純的責任鏈模式裏面,一個請求可以最終不被任何接收端對象所接收。
純的責任鏈模式的實際例子很難找到,一般看到的例子均是不純的責任鏈模式的實現。有些人認爲不純的責任鏈根本不是責任鏈模式,這也許是有道理的。但是在實際的系統裏,純的責任鏈很難找到。如果堅持責任鏈不純便不是責任鏈模式,那麼責任鏈模式便不會有太大意義了。