## 引入
在閻宏博士的《JAVA與模式》一書中開頭是這樣描述責任鏈(Chain of Responsibility)模式的:
> 責任鏈模式是一種對象的行為模式。在責任鏈模式里,很多對象由每一個對象對其下家的引用而連接起來形成一條鏈。請求在這個鏈上傳遞,直到鏈上的某一個對象決定處理此請求。發出這個請求的客戶端并不知道鏈上的哪一個對象最終處理這個請求,這使得系統可以在不影響客戶端的情況下動態地重新組織和分配責任。
## 定義
避免請求發送者與接收者耦合在一起,讓多個對象都有可能接收請求,將這些對象連接成一條鏈,并且沿著這條鏈傳遞請求,直到有對象處理它為止,這就是職責鏈模式。
在職責鏈模式中最關鍵的一點就是客戶提交請求后,請求沿著鏈往下傳遞直到有一個處理者處理它,在這里客戶無需關心它的請求是哪個處理者來處理,反正總有一個處理者會處理它的請求。
在這里客戶端和處理者都沒有對方明確的信息,同時處理者也不知道職責鏈中的結構。所以職責鏈可以簡化對象的相互連接,他們只需要保存一個指向其后續者的引用,而不需要保存所有候選者的引用。
在職責鏈模式中我們可以隨時隨地的增加或者更改一個處理者,甚至可以更改處理者的順序,增加了系統的靈活性。處理靈活性是增加了,但是有時候可能會導致一個請求無論如何也得不到處理,它會被放置在鏈末端,這個既是職責鏈的優點也是缺點。
## 結構
下面使用了一個責任鏈模式的最簡單的實現。

責任鏈模式涉及到的角色如下所示:
* 抽象處理者(Handler)角色:定義出一個處理請求的接口。如果需要,接口可以定義 出一個方法以設定和返回對下家的引用。這個角色通常由一個Java抽象類或者Java接口實現。上圖中Handler類的聚合關系給出了具體子類對下家的引用,抽象方法handleRequest()規范了子類處理請求的操作。
* 具體處理者(ConcreteHandler)角色:具體處理者接到請求后,可以選擇將請求處理掉,或者將請求傳給下家。由于具體處理者持有對下家的引用,因此,如果需要,具體處理者可以訪問下家。
## 代碼實現
來考慮這樣一個功能:申請聚餐費用的管理。
很多公司都是這樣的福利,就是項目組或者是部門可以向公司申請一些聚餐費用,用于組織項目組成員或者是部門成員進行聚餐活動。
申請聚餐費用的大致流程一般是:由申請人先填寫申請單,然后交給領導審批,如果申請批準下來,領導會通知申請人審批通過,然后申請人去財務領取費用,如果沒有批準下來,領導會通知申請人審批未通過,此事也就此作罷。
不同級別的領導,對于審批的額度是不一樣的,比如,項目經理只能審批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);
}
}
```
運行結果如下所示:

## 純的與不純的責任鏈模式
一個純的責任鏈模式要求一個具體的處理者對象只能在兩個行為中選擇一個:一是承擔責任,而是把責任推給下家。不允許出現某一個具體處理者對象在承擔了一部分責任后又 把責任向下傳的情況。
在一個純的責任鏈模式里面,一個請求必須被某一個處理者對象所接收;在一個不純的責任鏈模式里面,一個請求可以最終不被任何接收端對象所接收。
純的責任鏈模式的實際例子很難找到,一般看到的例子均是不純的責任鏈模式的實現。有些人認為不純的責任鏈根本不是責任鏈模式,這也許是有道理的。但是在實際的系統里,純的責任鏈很難找到。如果堅持責任鏈不純便不是責任鏈模式,那么責任鏈模式便不會有太大意義了。
## 優點
* 降低耦合度,客戶端不需要知道是誰處理了他的請求,只知道該請求被正確的處理了就可以了
* 可以簡化對象的相互連接,一個接收者只需要保存一個后面接收者的引用就可以了,而不是保留所有的接收者的引用
* 增強了給對象指派職責的靈活性,只需要在運行時刻在該鏈上增加或者修改接收者的引用,就可以調動他們的次序.以及增加或刪除接收者
## 缺點
* 不能保證客戶端發出的請求一定會被處理.因為這個請求沒有明確的接收者,
* 系統性能將受到一定的影響,可能會造成循環調用
## 適用場景
* 1、有多個對象可以處理同一個請求,具體哪個對象處理該請求由運行時刻自動確定。
* 2、在不明確指定接收者的情況下,向多個對象中的一個提交一個請求。
* 3、可動態指定一組對象處理請求。
## 總結
* 1、職責鏈模式將請求的發送者和接受者解耦了。客戶端不需要知道請求處理者的明確信息,甚至不需要知道鏈的結構,它只需要將請求進行發送即可。
* 2、職責鏈模式能夠非常方便的動態增加新職責或者刪除職責。
* 3、客戶端發送的請求可能會得不到處理。
* 4、處理者不需要知道鏈的結構,只需要明白他的后續者是誰就可以了。這樣就簡化了系統中的對象。
- java
- 設計模式
- 設計模式總覽
- 設計原則
- 工廠方法模式
- 抽象工廠模式
- 單例模式
- 建造者模式
- 原型模式
- 適配器模式
- 裝飾者模式
- 代理模式
- 外觀模式
- 橋接模式
- 組合模式
- 享元模式
- 策略模式
- 模板方法模式
- 觀察者模式
- 迭代子模式
- 責任鏈模式
- 命令模式
- 備忘錄模式
- 狀態模式
- 訪問者模式
- 中介者模式
- 解釋器模式
- 附錄
- JVM相關
- JVM內存結構
- Java虛擬機的內存組成以及堆內存介紹
- Java堆和棧
- 附錄-數據結構的堆棧和內存分配的堆區棧區的區別
- Java內存之Java 堆
- Java內存之虛擬機和內存區域概述
- Java 內存之方法區和運行時常量池
- Java 內存之直接內存(堆外內存)
- JAVA內存模型
- Java內存模型介紹
- 內存模型如何解決緩存一致性問題
- 深入理解Java內存模型——基礎
- 深入理解Java內存模型——重排序
- 深入理解Java內存模型——順序一致性
- 深入理解Java內存模型——volatile
- 深入理解Java內存模型——鎖
- 深入理解Java內存模型——final
- 深入理解Java內存模型——總結
- 內存可見性
- JAVA對象模型
- JVM內存結構 VS Java內存模型 VS Java對象模型
- Java的對象模型
- Java的對象頭
- HotSpot虛擬機
- HotSpot虛擬機對象探秘
- 深入分析Java的編譯原理
- Java虛擬機的鎖優化技術
- 對象和數組并不是都在堆上分配內存的
- 垃圾回收
- JVM內存管理及垃圾回收
- JVM 垃圾回收器工作原理及使用實例介紹
- JVM內存回收理論與實現(對象存活的判定)
- JVM參數及調優
- CMS GC日志分析
- JVM實用參數(一)JVM類型以及編譯器模式
- JVM實用參數(二)參數分類和即時(JIT)編譯器診斷
- JVM實用參數(三)打印所有XX參數及值
- JVM實用參數(四)內存調優
- JVM實用參數(五)新生代垃圾回收
- JVM實用參數(六) 吞吐量收集器
- JVM實用參數(七)CMS收集器
- JVM實用參數(八)GC日志
- Java性能調優原則
- JVM 優化經驗總結
- 面試題整理
- 面試題1
- java日志規約
- Spring安全
- OAtuth2.0簡介
- Spring Session 簡介(一)
- Spring Session 簡介(二)
- Spring Session 簡介(三)
- Spring Security 簡介(一)
- Spring Security 簡介(二)
- Spring Security 簡介(三)
- Spring Security 簡介(四)
- Spring Security 簡介(五)
- Spring Security Oauth2 (一)
- Spring Security Oauth2 (二)
- Spring Security Oauth2 (三)
- SpringBoot
- Shiro
- Shiro和Spring Security對比
- Shiro簡介
- Session、Cookie和Cache
- Web Socket
- Spring WebFlux