責任鏈模式(Chain of Responsibility Pattern)在《Head First設(shè)計模式》一書中被稱為“剩下的模式”峭梳,其實使用也是蠻多的御铃。最近在學習Netty的過程中用到了責任鏈模式渊抽,在此反過頭來重溫一下責任鏈模式蟆豫。
當你想要讓一個以上的對象有機會能夠處理某個請求的時候橘忱,就使用責任鏈模式顷扩。
一火邓、場景
借用《Head First設(shè)計模式》書中的典型場景:需要處理四種類型的電子郵件飞崖,第一種類型是粉絲寄來的信烂叔,表示他們喜歡新推出的游戲;第二種類型是父母寄來的信固歪,他們抱怨孩子總是沉迷游戲而忘記做作業(yè)蒜鸡;第三種類型是店家希望在其他地方也擺放糖果機胯努;第四種類型是垃圾郵件。現(xiàn)在已經(jīng)可以根據(jù)郵件內(nèi)容確定收到的郵件屬于哪種類型逢防,需要設(shè)計一個程序來處理這些郵件叶沛。
Talk is cheap. Show me the code.直接用代碼來說話吧。
用枚舉來定義四種類型的郵件:
public enum EmailEnum {
SPAM_EMAIL(1, "Spam_Email"),
FAN_EMAIL(2, "Fan_Email"),
COMPLAINT_EMAIL(3, "Complaint_Email"),
NEW_LOC_EMAIL(4, "New_Loc_Email");
private Integer code;
private String desc;
EmailEnum(Integer code, String desc) {
this.code = code;
this.desc = desc;
}
public Integer getCode() {
return code;
}
public String getDesc() {
return desc;
}
}
假設(shè)郵件有兩個屬性:郵件類型和郵件內(nèi)容忘朝,定義郵件:
public class Email {
int type;
String content;
public Email(int type, String content) {
this.type = type;
this.content = content;
}
public int getType() {
return type;
}
public String getContent() {
return content;
}
}
二灰署、不使用責任鏈模式
如果不采用責任鏈模式?使用EmailHandler這個類來統(tǒng)一處理上述四種郵件局嘁,程序是這樣子的:
public class EmailHandler {
public void handleEmai(Email email) {
if (EmailEnum.SPAM_EMAIL.getCode().equals(email.getType())) {
// 處理垃圾郵件
handleSpamEmail(email);
} else if (EmailEnum.FAN_EMAIL.getCode().equals(email.getType())) {
// 處理粉絲郵件
handleFanEmail(email);
} else if (EmailEnum.COMPLAINT_EMAIL.getCode().equals(email.getType())) {
// 處理抱怨郵件
handleComplaintEmail(email);
} else {
// 處理新增郵件
handleNewLocEmail(email);
}
}
private void handleNewLocEmail(Email email) {
System.out.println("handleNewLocEmail...");
// 處理代碼省略
}
private void handleComplaintEmail(Email email) {
System.out.println("handleComplaintEmail...");
// 處理代碼省略
}
private void handleFanEmail(Email email) {
System.out.println("handleFanEmail...");
// 處理代碼省略
}
public void handleSpamEmail(Email email) {
System.out.println("handleSpamEmail...");
// 處理代碼省略
}
}
這個類雖然強大溉箕,但是是不夠優(yōu)雅的:
(1)EmailHandler類較為龐大,各種類型郵件的處理都集中在一個類中悦昵,違反了“單一職責原則”肴茄。
(2)如果之后增加新的郵件類型、刪除某一種郵件類型旱捧,或者有其他新功能的拓展独郎,都必須修改源代碼并進行嚴格測試,違反了“開閉原則”枚赡。
開放-關(guān)閉原則:類應(yīng)該對擴展開放氓癌,對修改關(guān)閉。
三贫橙、使用責任鏈模式
如何進行改進呢贪婉?那就是使用責任鏈模式,為某個請求創(chuàng)建一個對象鏈卢肃。每個對象按照順序檢查這個請求疲迂,并對其處理,或者將它傳遞給鏈中的下一個對象莫湘。在本例中尤蒿,當收到電子郵件的時候,先進入第一個處理器SpamHandler幅垮,如果SpamHandler無法處理腰池,就將它傳給FanHandler,以此類推...
本例使用責任鏈模式的結(jié)構(gòu)圖如圖所示:
Handler是一個抽象的處理器忙芒,是一個抽象類示弓。抽象類中定義了一個抽象處理器的對象successor,通過該引用呵萨,可以形成一條責任鏈奏属。抽象類中還定義了抽象處理請求的的方法handleRequest()。代碼如下:
public abstract class Handler {
protected Handler successor;
public void setSuccessor(Handler successor) {
this.successor = successor;
}
public abstract void handleRequest(Email email);
}
SpamHandler潮峦、FanHandler囱皿、ComplaintHandler和NewLocHandler是具體的處理器勇婴,繼承抽象類Handler,用來處理具體的郵件請求铆帽。處理細節(jié):處理之前要進行類型的判斷咆耿,看是否能夠處理該請求,如果可以處理就處理爹橱,否則就轉(zhuǎn)發(fā)給后繼的處理器去處理萨螺。代碼如下:
SpamHandler
public class SpamHandler extends Handler{
@Override
public void handleRequest(Email email) {
if (EmailEnum.SPAM_EMAIL.getCode().equals(email.getType())) {
//處理請求
System.out.println("SpamHandler handleRequest...");
}
else {
this.successor.handleRequest(email); //轉(zhuǎn)發(fā)請求
}
}
}
FanHandler
public class FanHandler extends Handler{
@Override
public void handleRequest(Email email) {
if (EmailEnum.FAN_EMAIL.getCode().equals(email.getType())) {
//處理請求
System.out.println("FanHandler handleRequest...");
}
else {
this.successor.handleRequest(email); //轉(zhuǎn)發(fā)請求
}
}
}
ComplaintHandler
public class ComplaintHandler extends Handler{
@Override
public void handleRequest(Email email) {
if (EmailEnum.COMPLAINT_EMAIL.getCode().equals(email.getType())) {
//處理請求
System.out.println("ComplaintHandler handleRequest...");
}
else {
this.successor.handleRequest(email); //轉(zhuǎn)發(fā)請求
}
}
}
NewLocHandler
public class NewLocHandler extends Handler{
@Override
public void handleRequest(Email email) {
if (EmailEnum.NEW_LOC_EMAIL.getCode().equals(email.getType())) {
//處理請求
System.out.println("NewLocHandler handleRequest...");
}
else {
this.successor.handleRequest(email); //轉(zhuǎn)發(fā)請求
}
}
}
需要注意的是,責任鏈模式并不創(chuàng)建責任鏈愧驱,責任鏈的創(chuàng)建工作必須由系統(tǒng)的其他部分來完成慰技,一般是在使用該責任鏈的客戶端中創(chuàng)建責任鏈。責任鏈模式降低了請求的發(fā)送端和接收端之間的耦合组砚,使多個對象都有機會處理這個請求吻商。下面編寫測試類進行測試:
public class Test {
public static void main(String[] args) {
// 創(chuàng)建郵件處理請求
Email email1 = new Email(1,"aaa");
Email email2 = new Email(2,"bbb");
Email email3 = new Email(3,"ccc");
Email email4 = new Email(4,"ddd");
// 創(chuàng)建Handler
SpamHandler handler1 = new SpamHandler();
FanHandler handler2 = new FanHandler();
ComplaintHandler handler3 = new ComplaintHandler();
NewLocHandler handler4 = new NewLocHandler();
// 創(chuàng)建責任鏈
handler1.setSuccessor(handler2);
handler2.setSuccessor(handler3);
handler3.setSuccessor(handler4);
// 處理請求
handler1.handleRequest(email1);
handler1.handleRequest(email2);
handler1.handleRequest(email3);
handler1.handleRequest(email4);
}
}
在代碼中創(chuàng)建四種類型的郵件用于處理,創(chuàng)建了四種不同的處理器(SpamHandler糟红、FanHandler艾帐、ComplaintHandler、NewLocHandler)盆偿,形成“handler1 -> handler2 -> handler3 -> handler4”的責任鏈柒爸,使用這條責任鏈處理四種類型的郵件。運行結(jié)構(gòu)如下:
這樣處理之后事扭,明顯使得請求發(fā)送者和接受者解耦捎稚;每個實現(xiàn)類都有自己明確且獨一無二的職責;如果增加一個類型求橄,只需要再增加一個具體類去繼承Handler今野,書寫自己的處理邏輯,在責任鏈中進行添加罐农;如果刪除某種類型的条霜,只需要在構(gòu)建責任鏈的時候,把它刪除就可以了涵亏,實現(xiàn)動態(tài)增加或者刪除責任宰睡,符合設(shè)計模式的原則。
四溯乒、總結(jié)
責任鏈模式通過建立一條鏈來組織請求的處理者夹厌,請求將沿著鏈進行傳遞豹爹,請求發(fā)送者無須知道請求在何時裆悄、何處以及如何被處理,實現(xiàn)了請求發(fā)送者與處理者的解耦臂聋。在軟件開發(fā)中光稼,如果遇到有多個對象可以處理同一請求時可以應(yīng)用責任鏈模式或南,例如在Web應(yīng)用開發(fā)中創(chuàng)建一個過濾器鏈來對請求數(shù)據(jù)進行過濾,在工作流系統(tǒng)中實現(xiàn)公文的分級審批等等艾君,使用責任鏈模式可以較好地解決此類問題采够。