責(zé)任鏈模式(Chain of Responsibility)
在現(xiàn)實(shí)生活中铺浇,常常會(huì)出現(xiàn)這樣的事例:一個(gè)請(qǐng)求有多個(gè)對(duì)象可以處理,但每個(gè)對(duì)象的處理?xiàng)l件或權(quán)限不同明郭。例如嬉挡,公司員工請(qǐng)假,可批假的領(lǐng)導(dǎo)有部門(mén)負(fù)責(zé)人喷户、副總經(jīng)理擂橘、總經(jīng)理等,但每個(gè)領(lǐng)導(dǎo)能批準(zhǔn)的天數(shù)不同摩骨,員工必須根據(jù)自己要請(qǐng)假的天數(shù)去找不同的領(lǐng)導(dǎo)簽名,也就是說(shuō)員工必須記住每個(gè)領(lǐng)導(dǎo)的姓名朗若、電話和地址等信息恼五,這增加了難度。這樣的例子還有很多哭懈,如找領(lǐng)導(dǎo)出差報(bào)銷灾馒、生活中的“擊鼓傳花”游戲等。
在計(jì)算機(jī)軟硬件中也有相關(guān)例子遣总,如總線網(wǎng)中數(shù)據(jù)報(bào)傳送睬罗,每臺(tái)計(jì)算機(jī)根據(jù)目標(biāo)地址是否同自己的地址相同來(lái)決定是否接收轨功;還有異常處理中,處理程序根據(jù)異常的類型決定自己是否處理該異常容达;還有Struts2的攔截器古涧、JSP和Servlet的 Filter 等,所有這些花盐,如果用責(zé)任鏈模式都能很好解決羡滑。
模式的定義與特點(diǎn)
-
責(zé)任鏈(Chain of Responsibility)模式的定義:
為了避免請(qǐng)求發(fā)送者與多個(gè)請(qǐng)求處理者耦合在一起,將所有請(qǐng)求的處理者通過(guò)前一對(duì)象記住其下一個(gè)對(duì)象的引用而連成一條鏈算芯;當(dāng)有請(qǐng)求發(fā)生時(shí)柒昏,可將請(qǐng)求沿著這條鏈傳遞,直到有對(duì)象處理它為止熙揍。
在責(zé)任鏈模式中,客戶只需要將請(qǐng)求發(fā)送到責(zé)任鏈上即可届囚,無(wú)須關(guān)心請(qǐng)求的處理細(xì)節(jié)和請(qǐng)求的傳遞過(guò)程有梆,所以責(zé)任鏈將請(qǐng)求的發(fā)送者和請(qǐng)求的處理者解耦了。責(zé)任鏈模式是一種對(duì)象行為型模式奖亚。
- 責(zé)任鏈模式(Chain of Responsibility)的優(yōu)點(diǎn):
- 降低了對(duì)象之間的耦合度淳梦。該模式使得一個(gè)對(duì)象無(wú)須知道到底是哪一個(gè)對(duì)象處理其請(qǐng)求以及鏈的結(jié)構(gòu),發(fā)送者和接收者也無(wú)須擁有對(duì)方的明確信息昔字。
- 增強(qiáng)了系統(tǒng)的可擴(kuò)展性爆袍。可以根據(jù)需要增加新的請(qǐng)求處理類作郭,滿足開(kāi)閉原則陨囊。
- 增強(qiáng)了給對(duì)象指派職責(zé)的靈活性。當(dāng)工作流程發(fā)生變化夹攒,可以動(dòng)態(tài)地改變鏈內(nèi)的成員或者調(diào)動(dòng)它們的次序蜘醋,也可動(dòng)態(tài)地新增或者刪除責(zé)任。
- 責(zé)任鏈簡(jiǎn)化了對(duì)象之間的連接咏尝。每個(gè)對(duì)象只需保持一個(gè)指向其后繼者的引用压语,不需保持其他所有處理者的引用,這避免了使用眾多的 if 或者 if···else 語(yǔ)句编检。
- 責(zé)任分擔(dān)胎食。每個(gè)類只需要處理自己該處理的工作,不該處理的傳遞給下一個(gè)對(duì)象完成允懂,明確各類的責(zé)任范圍厕怜,符合類的單一職責(zé)原則。
- 責(zé)任鏈模式(Chain of Responsibility)的缺點(diǎn):
- 不能保證每個(gè)請(qǐng)求一定被處理。由于一個(gè)請(qǐng)求沒(méi)有明確的接收者粥航,所以不能保證它一定會(huì)被處理琅捏,該請(qǐng)求可能一直傳到鏈的末端都得不到處理。
- 對(duì)比較長(zhǎng)的職責(zé)鏈递雀,請(qǐng)求的處理可能涉及多個(gè)處理對(duì)象柄延,系統(tǒng)性能將受到一定影響。
- 職責(zé)鏈建立的合理性要靠客戶端來(lái)保證映之,增加了客戶端的復(fù)雜性拦焚,可能會(huì)由于職責(zé)鏈的錯(cuò)誤設(shè)置而導(dǎo)致系統(tǒng)出錯(cuò),如可能會(huì)造成循環(huán)調(diào)用杠输。
模式的結(jié)構(gòu)與實(shí)現(xiàn)
通常情況下赎败,可以通過(guò)數(shù)據(jù)鏈表來(lái)實(shí)現(xiàn)職責(zé)鏈模式的數(shù)據(jù)結(jié)構(gòu)。
1.模式的結(jié)構(gòu)
職責(zé)鏈模式主要包含以下角色蠢甲。
- 抽象處理者(Handler)角色: 定義一個(gè)處理請(qǐng)求的接口僵刮,包含抽象處理方法和一個(gè)后繼連接。
- 具體處理者(Concrete Handler)角色: 實(shí)現(xiàn)抽象處理者的處理方法鹦牛,判斷能否處理本次請(qǐng)求搞糕,如果可以處理請(qǐng)求則處理,否則將該請(qǐng)求轉(zhuǎn)給它的后繼者曼追。
- 客戶類(Client)角色: 創(chuàng)建處理鏈窍仰,并向鏈頭的具體處理者對(duì)象提交請(qǐng)求,它不關(guān)心處理細(xì)節(jié)和請(qǐng)求的傳遞過(guò)程礼殊。
其結(jié)構(gòu)圖如圖 1 所示驹吮。客戶端可按圖 2 所示設(shè)置責(zé)任鏈晶伦。
2. 模式的實(shí)現(xiàn)
職責(zé)鏈模式的實(shí)現(xiàn)代碼如下:
package chainOfResponsibility;
public class ChainOfResponsibilityPattern
{
public static void main(String[] args)
{
//組裝責(zé)任鏈
Handler handler1=new ConcreteHandler1();
Handler handler2=new ConcreteHandler2();
handler1.setNext(handler2);
//提交請(qǐng)求
handler1.handleRequest("two");
}
}
//抽象處理者角色
abstract class Handler
{
private Handler next;
public void setNext(Handler next)
{
this.next=next;
}
public Handler getNext()
{
return next;
}
//處理請(qǐng)求的方法
public abstract void handleRequest(String request);
}
//具體處理者角色1
class ConcreteHandler1 extends Handler
{
public void handleRequest(String request)
{
if(request.equals("one"))
{
System.out.println("具體處理者1負(fù)責(zé)處理該請(qǐng)求碟狞!");
}
else
{
if(getNext()!=null)
{
getNext().handleRequest(request);
}
else
{
System.out.println("沒(méi)有人處理該請(qǐng)求!");
}
}
}
}
//具體處理者角色2
class ConcreteHandler2 extends Handler
{
public void handleRequest(String request)
{
if(request.equals("two"))
{
System.out.println("具體處理者2負(fù)責(zé)處理該請(qǐng)求婚陪!");
}
else
{
if(getNext()!=null)
{
getNext().handleRequest(request);
}
else
{
System.out.println("沒(méi)有人處理該請(qǐng)求族沃!");
}
}
}
}
程序運(yùn)行結(jié)果如下:
具體處理者2負(fù)責(zé)處理該請(qǐng)求!
模式的應(yīng)用實(shí)例
【例】用責(zé)任鏈模式設(shè)計(jì)一個(gè)請(qǐng)假條審批模塊泌参。
分析:假如規(guī)定學(xué)生請(qǐng)假小于或等于 2 天脆淹,班主任可以批準(zhǔn);小于或等于 7 天沽一,系主任可以批準(zhǔn)盖溺;小于或等于 10 天,院長(zhǎng)可以批準(zhǔn)锯玛;其他情況不予批準(zhǔn);這個(gè)實(shí)例適合使用職責(zé)鏈模式實(shí)現(xiàn)。
首先攘残,定義一個(gè)領(lǐng)導(dǎo)類(Leader)拙友,它是抽象處理者,包含了一個(gè)指向下一位領(lǐng)導(dǎo)的指針 next 和一個(gè)處理假條的抽象處理方法 handleRequest(int LeaveDays)歼郭;然后遗契,定義班主任類(ClassAdviser)、系主任類(DepartmentHead)和院長(zhǎng)類(Dean)病曾,它們是抽象處理者的子類牍蜂,是具體處理者,必須根據(jù)自己的權(quán)力去實(shí)現(xiàn)父類的 handleRequest(int LeaveDays) 方法泰涂,如果無(wú)權(quán)處理就將假條交給下一位具體處理者鲫竞,直到最后;客戶類負(fù)責(zé)創(chuàng)建處理鏈逼蒙,并將假條交給鏈頭的具體處理者(班主任)从绘。圖 3 所示是其結(jié)構(gòu)圖。
程序代碼如下:
package chainOfResponsibility;
public class LeaveApprovalTest
{
public static void main(String[] args)
{
//組裝責(zé)任鏈
Leader teacher1=new ClassAdviser();
Leader teacher2=new DepartmentHead();
Leader teacher3=new Dean();
//Leader teacher4=new DeanOfStudies();
teacher1.setNext(teacher2);
teacher2.setNext(teacher3);
//teacher3.setNext(teacher4);
//提交請(qǐng)求
teacher1.handleRequest(8);
}
}
//抽象處理者:領(lǐng)導(dǎo)類
abstract class Leader
{
private Leader next;
public void setNext(Leader next)
{
this.next=next;
}
public Leader getNext()
{
return next;
}
//處理請(qǐng)求的方法
public abstract void handleRequest(int LeaveDays);
}
//具體處理者1:班主任類
class ClassAdviser extends Leader
{
public void handleRequest(int LeaveDays)
{
if(LeaveDays<=2)
{
System.out.println("班主任批準(zhǔn)您請(qǐng)假" + LeaveDays + "天是牢。");
}
else
{
if(getNext() != null)
{
getNext().handleRequest(LeaveDays);
}
else
{
System.out.println("請(qǐng)假天數(shù)太多僵井,沒(méi)有人批準(zhǔn)該假條!");
}
}
}
}
//具體處理者2:系主任類
class DepartmentHead extends Leader
{
public void handleRequest(int LeaveDays)
{
if(LeaveDays<=7)
{
System.out.println("系主任批準(zhǔn)您請(qǐng)假" + LeaveDays + "天驳棱。");
}
else
{
if(getNext() != null)
{
getNext().handleRequest(LeaveDays);
}
else
{
System.out.println("請(qǐng)假天數(shù)太多批什,沒(méi)有人批準(zhǔn)該假條!");
}
}
}
}
//具體處理者3:院長(zhǎng)類
class Dean extends Leader
{
public void handleRequest(int LeaveDays)
{
if(LeaveDays<=10)
{
System.out.println("院長(zhǎng)批準(zhǔn)您請(qǐng)假" + LeaveDays + "天社搅。");
}
else
{
if(getNext() != null)
{
getNext().handleRequest(LeaveDays);
}
else
{
System.out.println("請(qǐng)假天數(shù)太多驻债,沒(méi)有人批準(zhǔn)該假條!");
}
}
}
}
//具體處理者4:教務(wù)處長(zhǎng)類
class DeanOfStudies extends Leader
{
public void handleRequest(int LeaveDays)
{
if(LeaveDays<=20)
{
System.out.println("教務(wù)處長(zhǎng)批準(zhǔn)您請(qǐng)假"+LeaveDays+"天罚渐。");
}
else
{
if(getNext()!=null)
{
getNext().handleRequest(LeaveDays);
}
else
{
System.out.println("請(qǐng)假天數(shù)太多却汉,沒(méi)有人批準(zhǔn)該假條!");
}
}
}
}
程序運(yùn)行結(jié)果如下:
院長(zhǎng)批準(zhǔn)您請(qǐng)假8天荷并。
假如增加一個(gè)教務(wù)處長(zhǎng)類合砂,可以批準(zhǔn)學(xué)生請(qǐng)假 20 天,也非常簡(jiǎn)單源织,代碼如下:
//具體處理者4:教務(wù)處長(zhǎng)類
class DeanOfStudies extends Leader
{
public void handleRequest(int LeaveDays)
{
if(LeaveDays<=20)
{
System.out.println("教務(wù)處長(zhǎng)批準(zhǔn)您請(qǐng)假"+LeaveDays+"天翩伪。");
}
else
{
if(getNext()!=null)
{
getNext().handleRequest(LeaveDays);
}
else
{
System.out.println("請(qǐng)假天數(shù)太多,沒(méi)有人批準(zhǔn)該假條谈息!");
}
}
}
}
模式的應(yīng)用場(chǎng)景
- 有多個(gè)對(duì)象可以處理一個(gè)請(qǐng)求缘屹,哪個(gè)對(duì)象處理該請(qǐng)求由運(yùn)行時(shí)刻自動(dòng)確定。
- 可動(dòng)態(tài)指定一組對(duì)象處理請(qǐng)求侠仇,或添加新的處理者轻姿。
- 在不明確指定請(qǐng)求處理者的情況下犁珠,向多個(gè)處理者中的一個(gè)提交請(qǐng)求。
模式的擴(kuò)展
職責(zé)鏈模式存在以下兩種情況互亮。
- 純的職責(zé)鏈模式:一個(gè)請(qǐng)求必須被某一個(gè)處理者對(duì)象所接收犁享,且一個(gè)具體處理者對(duì)某個(gè)請(qǐng)求的處理只能采用以下兩種行為之一:自己處理(承擔(dān)責(zé)任);把責(zé)任推給下家處理豹休。
- 不純的職責(zé)鏈模式:允許出現(xiàn)某一個(gè)具體處理者對(duì)象在承擔(dān)了請(qǐng)求的一部分責(zé)任后又將剩余的責(zé)任傳給下家的情況炊昆,且一個(gè)請(qǐng)求可以最終不被任何接收端對(duì)象所接收。