責任鏈模式(Chain of Responsibility Pattern)在《Head First設計模式》一書中被稱爲「剩下的模式」,其實使用也是蠻多的。最近在學習Netty的過程當中用到了責任鏈模式,在此反過頭來重溫一下責任鏈模式。設計模式
當你想要讓一個以上的對象有機會可以處理某個請求的時候,就使用責任鏈模式。ide
借用《Head First設計模式》書中的典型場景:須要處理四種類型的電子郵件,第一種類型是粉絲寄來的信,表示他們喜歡新推出的遊戲;第二種類型是父母寄來的信,他們抱怨孩子老是沉迷遊戲而忘記作做業;第三種類型是店家但願在其餘地方也擺放糖果機;第四種類型是垃圾郵件。如今已經能夠根據郵件內容肯定收到的郵件屬於哪一種類型,須要設計一個程序來處理這些郵件。學習
Talk is cheap. Show me the code.直接用代碼來講話吧。測試
用枚舉來定義四種類型的郵件:this
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; } }
假設郵件有兩個屬性:郵件類型和郵件內容,定義郵件:設計
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這個類來統一處理上述四種郵件,程序是這樣子的:code
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..."); // 處理代碼省略 } }
這個類雖然強大,可是是不夠優雅的:對象
(1)EmailHandler類較爲龐大,各類類型郵件的處理都集中在一個類中,違反了「單一職責原則」。blog
(2)若是以後增長新的郵件類型、刪除某一種郵件類型,或者有其餘新功能的拓展,都必須修改源代碼並進行嚴格測試,違反了「開閉原則」。繼承
開放-關閉原則:類應該對擴展開放,對修改關閉。
如何進行改進呢?那就是使用責任鏈模式,爲某個請求建立一個對象鏈。每一個對象按照順序檢查這個請求,並對其處理,或者將它傳遞給鏈中的下一個對象。在本例中,當收到電子郵件的時候,先進入第一個處理器SpamHandler,若是SpamHandler沒法處理,就將它傳給FanHandler,以此類推...
本例使用責任鏈模式的結構圖如圖所示:
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,用來處理具體的郵件請求。處理細節:處理以前要進行類型的判斷,看是否可以處理該請求,若是能夠處理就處理,不然就轉發給後繼的處理器去處理。代碼以下:
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); //轉發請求 } } }
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); //轉發請求 } } }
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); //轉發請求 } } }
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); //轉發請求 } } }
須要注意的是,責任鏈模式並不建立責任鏈,責任鏈的建立工做必須由系統的其餘部分來完成,通常是在使用該責任鏈的客戶端中建立責任鏈。責任鏈模式下降了請求的發送端和接收端之間的耦合,使多個對象都有機會處理這個請求。下面編寫測試類進行測試:
public class Test { public static void main(String[] args) { // 建立郵件處理請求 Email email1 = new Email(1,"aaa"); Email email2 = new Email(2,"bbb"); Email email3 = new Email(3,"ccc"); Email email4 = new Email(4,"ddd"); // 建立Handler SpamHandler handler1 = new SpamHandler(); FanHandler handler2 = new FanHandler(); ComplaintHandler handler3 = new ComplaintHandler(); NewLocHandler handler4 = new NewLocHandler(); // 建立責任鏈 handler1.setSuccessor(handler2); handler2.setSuccessor(handler3); handler3.setSuccessor(handler4); // 處理請求 handler1.handleRequest(email1); handler1.handleRequest(email2); handler1.handleRequest(email3); handler1.handleRequest(email4); } }
在代碼中建立四種類型的郵件用於處理,建立了四種不一樣的處理器(SpamHandler、FanHandler、ComplaintHandler、NewLocHandler),造成「handler1 -> handler2 -> handler3 -> handler4」的責任鏈,使用這條責任鏈處理四種類型的郵件。運行結構以下:
這樣處理以後,明顯使得請求發送者和接受者解耦;每一個實現類都有本身明確且獨一無二的職責;若是增長一個類型,只須要再增長一個具體類去繼承Handler,書寫本身的處理邏輯,在責任鏈中進行添加;若是刪除某種類型的,只須要在構建責任鏈的時候,把它刪除就能夠了,實現動態增長或者刪除責任,符合設計模式的原則。
責任鏈模式經過創建一條鏈來組織請求的處理者,請求將沿着鏈進行傳遞,請求發送者無須知道請求在什麼時候、何處以及如何被處理,實現了請求發送者與處理者的解耦。在軟件開發中,若是遇到有多個對象能夠處理同一請求時能夠應用責任鏈模式,例如在Web應用開發中建立一個過濾器鏈來對請求數據進行過濾,在工做流系統中實現公文的分級審批等等,使用責任鏈模式能夠較好地解決此類問題。