目錄
本文的結(jié)構(gòu)如下:
- 引言
- 什么是責(zé)任鏈模式
- 模式的結(jié)構(gòu)
- 典型代碼
- 代碼示例
- 純與不純的責(zé)任鏈模式
- 優(yōu)點(diǎn)和缺點(diǎn)
- 適用環(huán)境
- 模式應(yīng)用
一、引言
現(xiàn)在的網(wǎng)絡(luò)小說(shuō)套路大概是這樣的,小嘍啰惹上主角,主角霸氣側(cè)漏,打跑了;小嘍啰找來(lái)門中厲害的親爹,親爹上場(chǎng),過(guò)了兩招一看打不過(guò),一起跑路;但這口氣咽不下,一咬牙找來(lái)爺爺(一般是長(zhǎng)老或者宗主),宗主那當(dāng)然厲害了,主角打不過(guò),狼狽逃命,下定功夫苦加修煉又或者奇遇連連,功力大增,殺上山門怒殺全宗,報(bào)仇雪恨。

這種自己打不過(guò),找爸爸,爸爸打不過(guò)找爺爺?shù)哪J骄褪秦?zé)任鏈模式。
二、什么是責(zé)任鏈模式
很多情況下,在一個(gè)軟件系統(tǒng)中可以處理某個(gè)請(qǐng)求的對(duì)象不止一個(gè),比如上面跟主角打架,小嘍啰、小嘍啰他爸、小嘍啰他爺都可以跟主角打架,打不打得過(guò)不管(處不處理不管),主角沿著這條鏈進(jìn)行傳遞,這條鏈就稱為職責(zé)鏈。
職責(zé)鏈可以是一條直線、一個(gè)環(huán)或者一個(gè)樹形結(jié)構(gòu),最常見(jiàn)的職責(zé)鏈?zhǔn)侵本€型,即沿著一條單向的鏈來(lái)傳遞請(qǐng)求。鏈上的每一個(gè)對(duì)象都是請(qǐng)求處理者,責(zé)任鏈模式可以將請(qǐng)求的處理者組織成一條鏈,并讓請(qǐng)求沿著鏈傳遞,由鏈上的處理者對(duì)請(qǐng)求進(jìn)行相應(yīng)的處理,客戶端無(wú)須關(guān)心請(qǐng)求的處理細(xì)節(jié)以及請(qǐng)求的傳遞,只需將請(qǐng)求發(fā)送到鏈上即可,實(shí)現(xiàn)請(qǐng)求發(fā)送者和請(qǐng)求處理者解耦。
責(zé)任鏈模式定義如下:
責(zé)任鏈模式(Chain of Responsibility Pattern):避免請(qǐng)求發(fā)送者與接收者耦合在一起,讓多個(gè)對(duì)象都有可能接收請(qǐng)求,將這些對(duì)象連接成一條鏈,并且沿著這條鏈傳遞請(qǐng)求,直到有對(duì)象處理它為止。責(zé)任鏈模式是一種對(duì)象行為型模式。
三、模式的結(jié)構(gòu)
責(zé)任鏈模式結(jié)構(gòu)的核心在于引入了一個(gè)抽象處理者,責(zé)任鏈模式的UML類圖如下:

在責(zé)任鏈模式結(jié)構(gòu)圖中包含如下幾個(gè)角色:
- Handler(抽象處理者):定義一個(gè)處理請(qǐng)求的接口,一般為抽象類,請(qǐng)求處理方法為抽象方法,不同的具體處理者實(shí)現(xiàn)具體的請(qǐng)求處理。因?yàn)槊恳粋€(gè)處理者的下家還是一個(gè)處理者,因此在抽象處理者中定義了一個(gè)抽象處理者類型的對(duì)象,作為其對(duì)下家的引用。通過(guò)該引用,處理者可以連成一條鏈。
- ConcreteHandler(具體處理者):它是抽象處理者的子類,可以處理用戶請(qǐng)求,在具體處理者類中實(shí)現(xiàn)了抽象處理者中定義的抽象請(qǐng)求處理方法,在處理請(qǐng)求之前需要進(jìn)行判斷,看是否有相應(yīng)的處理權(quán)限,如果可以處理請(qǐng)求就處理它,否則將請(qǐng)求轉(zhuǎn)發(fā)給后繼者;在具體處理者中可以訪問(wèn)鏈中下一個(gè)對(duì)象,以便請(qǐng)求的轉(zhuǎn)發(fā)。
要形成鏈?zhǔn)秸{(diào)用,關(guān)鍵在于每一個(gè)處理者中有一個(gè)下一個(gè)處理者的引用。
四、典型代碼
抽象處理者是核心,典型代碼如下:
public abstract class Handler {
protected Handler successor;
public void setSuccessor(){
this.successor = successor;
}
public Handler getSuccessor(){
return successor;
}
public abstract void handler(String condition);
}
具體處理者繼承抽象處理者,主要作用有兩個(gè):第一是處理請(qǐng)求,如果能夠處理就自己處理了;第二是轉(zhuǎn)發(fā)請(qǐng)求,如果該請(qǐng)求超出了當(dāng)前處理者類的權(quán)限,可以將該請(qǐng)求轉(zhuǎn)發(fā)給下家。具體處理者類典型代碼如下:
public class ConcreteHandlerA extends Handler {
public void handler(String condition) {
if ("conditionA".equals(condition)){
System.out.println("處理請(qǐng)求");
}else {
System.out.println("轉(zhuǎn)發(fā)請(qǐng)求");
successor.handler(condition);
}
}
}
public class ConcreteHandlerB extends Handler {
public void handler(String condition) {
if ("conditionB".equals(condition)){
System.out.println("處理請(qǐng)求");
}else {
System.out.println("轉(zhuǎn)發(fā)請(qǐng)求");
successor.handler(condition);
}
}
}
責(zé)任鏈模式并不創(chuàng)建職責(zé)鏈,職責(zé)鏈的創(chuàng)建工作必須由系統(tǒng)的其他部分來(lái)完成,一般是在使用該職責(zé)鏈的客戶端中創(chuàng)建職責(zé)鏈。
五、代碼示例
以引言中的找主角打架為例。
5.1、不用責(zé)任鏈模式
public class Protagonist {
private int forceValue;//主角武力值
private String name;
private int age;
public Protagonist(String name, int age, int forceValue) {
this.name = name;
this.age = age;
this.forceValue = forceValue;
}
public void setForceValue(int forceValue) {
this.forceValue = forceValue;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public int getForceValue() {
return forceValue;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
public class FightHandler {
public void fightWith(Protagonist protagonist){
if (protagonist.getForceValue() < 100){//主角武力值小于100,自己上
selfFighting(protagonist);
}else if (protagonist.getForceValue() < 200){//找爸爸
fatherFighting(protagonist);
}else if(protagonist.getForceValue() < 400){
grandfatherFighting(protagonist);
} else {
togetherFighting(protagonist);
}
}
private void selfFighting(Protagonist protagonist){
//todo
System.out.println("我打得" + protagonist.getName() +"屁滾尿流,哭爹喊娘");
}
private void fatherFighting(Protagonist protagonist){
//todo
System.out.println("我爸打得" + protagonist.getName() +"屁滾尿流,哭爹喊娘");
}
private void grandfatherFighting(Protagonist protagonist){
//todo
System.out.println("我爺打得" + protagonist.getName() +"屁滾尿流,哭爹喊娘");
}
private void togetherFighting(Protagonist protagonist){
//todo
System.out.println("我們一起打得" + protagonist.getName() +"屁滾尿流,哭爹喊娘");
}
}
看到這段代碼,相信都會(huì)發(fā)現(xiàn)一些問(wèn)題存在:
- FightHandler類較為龐大,所有的打架方法都集中在這個(gè)類中,測(cè)試和維護(hù)不方便,也違背了“單一職責(zé)原則”。
- 加入主角生死間突然頓悟,武力值得到極大提升,想要找更厲害的人來(lái)和主角打架,又或者調(diào)整同主角打架的武力判斷值,都需要修改源碼,違背了“開閉原則”。
- 打架的流程固定,顯示小嘍啰自己上,再是他爸上,再是他爺上,如果一開始就他爺上(說(shuō)不定主角死,全書完),需要修改源碼,不靈活。
5.2、使用責(zé)任鏈模式

先定義抽象處理者:
public abstract class FightHandler {
protected FightHandler successor;
public void setSuccessor(FightHandler successor){
this.successor = successor;
}
public abstract void fightWith(Protagonist protagonist);
}
幾個(gè)具體處理者:
public class SelfFightHandler extends FightHandler {
public void fightWith(Protagonist protagonist) {
if (protagonist.getForceValue() < 100){
//todo
System.out.println("我打得" + protagonist.getName() +"屁滾尿流,哭爹喊娘");
}else {
successor.fightWith(protagonist);
}
}
}
public class FatherFightHandler extends FightHandler{
public void fightWith(Protagonist protagonist) {
if (protagonist.getForceValue() < 200){
//todo
System.out.println("我爸打得" + protagonist.getName() +"屁滾尿流,哭爹喊娘");
}else {
successor.fightWith(protagonist);
}
}
}
public class GrandfatherFightHandler extends FightHandler {
public void fightWith(Protagonist protagonist) {
if (protagonist.getForceValue() < 400){
//todo
System.out.println("我爺打得" + protagonist.getName() +"屁滾尿流,哭爹喊娘");
}else {
successor.fightWith(protagonist);
}
}
}
public class TogetherFightHandler extends FightHandler {
public void fightWith(Protagonist protagonist) {
if (protagonist.getForceValue() > 400){
//todo
System.out.println("我們一起打得" + protagonist.getName() +"屁滾尿流,哭爹喊娘");
}else {
successor.fightWith(protagonist);
}
}
}
客戶端測(cè)試:
public class Client {
public static void main(String[] args) {
FightHandler selfHandler = new SelfFightHandler();
FightHandler fatherHandler = new FatherFightHandler();
FightHandler grandfatherHandler = new GrandfatherFightHandler();
FightHandler togetherHandler = new TogetherFightHandler();
//創(chuàng)建職責(zé)鏈
selfHandler.setSuccessor(fatherHandler);
fatherHandler.setSuccessor(grandfatherHandler);
grandfatherHandler.setSuccessor(togetherHandler);
//主角
Protagonist zxf = new Protagonist("張小凡", 12, 88);
selfHandler.fightWith(zxf);
Protagonist qf = new Protagonist("喬峰", 33, 188);
selfHandler.fightWith(qf);
Protagonist zbj = new Protagonist("豬八戒", 500, 288);
selfHandler.fightWith(zbj);
}
}
這時(shí)新增一個(gè)具體處理者只需要繼承抽象處理者即可,由于鏈的創(chuàng)建過(guò)程由客戶端負(fù)責(zé),因此增加新的具體處理者類對(duì)原有類庫(kù)無(wú)任何影響,無(wú)須修改已有類的源代碼,符合“開閉原則”。
六、純與不純的責(zé)任鏈模式
責(zé)任鏈模式可分為純的責(zé)任鏈模式和不純的責(zé)任鏈模式兩種:
6.1、純的責(zé)任鏈模式
一個(gè)純的責(zé)任鏈模式要求:
- 一個(gè)具體處理者對(duì)象只能在“處理請(qǐng)求”和“轉(zhuǎn)發(fā)請(qǐng)求”中兩選一。
- 一個(gè)請(qǐng)求必須被某一個(gè)處理者對(duì)象所接收,不能出現(xiàn)某個(gè)請(qǐng)求未被任何一個(gè)處理者對(duì)象處理的情況。
6.2、不純的責(zé)任鏈模式
在一個(gè)不純的責(zé)任鏈模式中則允許:
- 某個(gè)請(qǐng)求被一個(gè)具體處理者部分處理后再向下傳遞。
- 或者一個(gè)具體處理者處理完某請(qǐng)求后其后繼處理者可以繼續(xù)處理該請(qǐng)求。
- 或者一個(gè)請(qǐng)求可以最終不被任何處理者對(duì)象所接收。
純的責(zé)任鏈模式的實(shí)際例子很難找到,一般看到的例子均是不純的責(zé)任鏈模式的實(shí)現(xiàn)。
七、優(yōu)點(diǎn)和缺點(diǎn)
7.1、優(yōu)點(diǎn)
責(zé)任鏈模式的主要優(yōu)點(diǎn)如下:
- 責(zé)任鏈模式使得一個(gè)對(duì)象無(wú)須知道是其他哪一個(gè)對(duì)象處理其請(qǐng)求,對(duì)象僅需知道該請(qǐng)求會(huì)被處理即可,接收者和發(fā)送者都沒(méi)有對(duì)方的明確信息,且鏈中的對(duì)象不需要知道鏈的結(jié)構(gòu),由客戶端負(fù)責(zé)鏈的創(chuàng)建,降低了系統(tǒng)的耦合度。
- 請(qǐng)求處理對(duì)象僅需維持一個(gè)指向其后繼者的引用,而不需要維持它對(duì)所有的候選處理者的引用,可簡(jiǎn)化對(duì)象的相互連接。
- 在給對(duì)象分派職責(zé)時(shí),職責(zé)鏈可以給我們更多的靈活性,可以通過(guò)在運(yùn)行時(shí)對(duì)該鏈進(jìn)行動(dòng)態(tài)的增加或修改來(lái)增加或改變處理一個(gè)請(qǐng)求的職責(zé)。
- 在系統(tǒng)中增加一個(gè)新的具體請(qǐng)求處理者時(shí)無(wú)須修改原有系統(tǒng)的代碼,只需要在客戶端重新建鏈即可,從這一點(diǎn)來(lái)看是符合“開閉原則”的。
7.2、缺點(diǎn)
責(zé)任鏈模式的主要缺點(diǎn)如下:
- 由于一個(gè)請(qǐng)求沒(méi)有明確的接收者,那么就不能保證它一定會(huì)被處理,該請(qǐng)求可能一直到鏈的末端都得不到處理;一個(gè)請(qǐng)求也可能因職責(zé)鏈沒(méi)有被正確配置而得不到處理。
- 對(duì)于比較長(zhǎng)的職責(zé)鏈,請(qǐng)求的處理可能涉及到多個(gè)處理對(duì)象,系統(tǒng)性能將受到一定影響,而且在進(jìn)行代碼調(diào)試時(shí)不太方便。
- 如果建鏈不當(dāng),可能會(huì)造成循環(huán)調(diào)用,將導(dǎo)致系統(tǒng)陷入死循環(huán)。
八、適用環(huán)境
在以下情況下可以考慮使用責(zé)任鏈模式:
- 有多個(gè)對(duì)象可以處理同一個(gè)請(qǐng)求,具體哪個(gè)對(duì)象處理該請(qǐng)求待運(yùn)行時(shí)刻再確定,客戶端只需將請(qǐng)求提交到鏈上,而無(wú)須關(guān)心請(qǐng)求的處理對(duì)象是誰(shuí)以及它是如何處理的。
- 在不明確指定接收者的情況下,向多個(gè)對(duì)象中的一個(gè)提交請(qǐng)求。
- 可動(dòng)態(tài)指定一組對(duì)象處理請(qǐng)求,客戶端可以動(dòng)態(tài)創(chuàng)建職責(zé)鏈來(lái)處理請(qǐng)求,還可以改變鏈中處理者之間的先后次序。
九、模式應(yīng)用
責(zé)任鏈設(shè)計(jì)模式(Chain of Responsibility)的應(yīng)用有:Java Web中的過(guò)濾器鏈、Struts2中的攔截器棧。