簡說設計模式——職責鏈模式

1、什麼是職責鏈模式

  從文字角度出發,咱們能夠先將關注點放在「鏈」字上,很容易聯想到鏈式結構,舉個生活中常見的例子,擊鼓傳花遊戲就是一個很典型的鏈式結構,全部人造成一條鏈,相互傳遞。而從另外一個角度說,職責鏈就是所謂的多級結構,好比去醫院開具病假條,普通醫生只能開一天的證實,若是須要更多時常,則需將開具職責轉交到上級去,上級醫師只能開三天證實,如需更多時常,則需將職責轉交到他的上級,以此類推,這就是一個職責鏈模式的典型應用。再好比公司請假,根據請假時常的不一樣,須要遞交到的級別也不一樣,這種層級遞進的關係就是一種多級結構。git

  職責鏈模式(Chain Of Responsibility),使多個對象都有機會處理請求,從而避免請求的發送者和接收者之間的耦合關係。將這個對象連成一條鏈,並沿着這條鏈傳遞該請求,直到有一個對象處理它爲止。UML結構圖以下:dom

  其中,Handler是抽象處理者,定義了一個處理請求的接口;ConcreteHandler是具體處理者,處理它所負責的請求,可訪問它的後繼者,若是可處理該請求就處理,不然就將該請求轉發給它的後繼者。ide

  1. 抽象處理者

  抽象處理者實現了三個職責:性能

  • 定義一個請求的處理方法handlerMessage(),是惟一對外開放的方法
  • 定義一個鏈的編排方式setNext(),用於設置下一個處理者
  • 定義了具體的請求者必須實現的兩個方法,即定義本身可以處理的級別的getHandlerLevel()方法及具體的處理任務echo()方法
 1 public abstract class Handler {
 2     
 3     private Handler nextHandler;    //下一個處理者
 4     
 5     public final Response handlerMessage(Request request) {
 6         Response response = null;
 7         
 8         if(this.getHandlerLevel().equals(request.getRequestLevel())) {    //判斷是不是本身的處理級別
 9             response = this.echo(request);
10         } else {
11             if(this.nextHandler != null) {    //下一處理者不爲空
12                 response = this.nextHandler.handlerMessage(request);
13             } else {
14                 //沒有適當的處理者,業務自行處理
15             }
16         }
17         
18         return response;
19     }
20     
21     //設定下一個處理者
22     public void setNext(Handler handler) {
23         this.nextHandler = handler;
24     }
25     
26     //每一個處理者的處理等級
27     protected abstract Level getHandlerLevel();
28     
29     //每一個處理者都必須實現的處理任務
30     protected abstract Response echo(Request request);
31 
32 }

  2. 具體處理者

  這裏咱們定義三個具體處理者,以便能組成一條鏈,ConcreteHandlerB及ConcreteHandlerC就再也不贅述了。測試

 1 public class ConcreteHandlerA extends Handler {
 2 
 3     @Override
 4     protected Level getHandlerLevel() {
 5         //設置本身的處理級別
 6         return null;
 7     }
 8 
 9     @Override
10     protected Response echo(Request request) {
11         //完成處理邏輯
12         return null;
13     }
14 
15 }

  3. Level類

  Level類負責定義請求和處理級別,具體內容需根據業務產生。this

1 public class Level {
2     //定義一個請求和處理等級
3 }

  4. Request類

  Request類負責封裝請求,具體內容需根據業務產生。spa

1 public class Request {
2     
3     //請求的等級
4     public Level getRequestLevel() {
5         return null;
6     }
7     
8 }

  5. Response類

  Response類負責封裝鏈中返回的結果,具體內容需根據業務產生。3d

1 public class Response {
2     //處理者返回的數據
3 }

  6. Client客戶端

  咱們在場景類或高層模塊中對類進行組裝,並傳遞請求,返回結果。以下對三個具體處理者進行組裝,按照1→2→3的順序,並得出返回結果。調試

 1 public class Client {
 2     
 3     public static void main(String[] args) {
 4         Handler handler1 = new ConcreteHandlerA();
 5         Handler handler2 = new ConcreteHandlerB();
 6         Handler handler3 = new ConcreteHandlerC();
 7         
 8         //設置鏈中的階段順序 1->2->3
 9         handler1.setNext(handler2);
10         handler2.setNext(handler3);
11         
12         //提交請求返回結果
13         Response response = handler1.handlerMessage(new Request());
14     }
15 
16 }

  固然這是個未完成的模板,最終結果會由於 request.getRequestLevel() 爲空而拋出異常,具體內容需根據業務邏輯進行編寫。code

 2、職責鏈模式的應用

  1. 什麼時候使用

  • 處理消息時

  2. 方法

  • 攔截的類都實現同一接口

  3. 優勢

  • 將請求和處理分開,實現解耦,提升系統的靈活性
  • 簡化了對象,使對象不須要知道鏈的結構

  4. 缺點

  • 性能會收到影響,特別是在鏈比較長的時候
  • 調試不方便。採用了相似遞歸的方式,調試時邏輯可能比較複雜
  • 不能保證請求必定被接收

  5. 使用場景

  • 有多個對象能夠處理同一個請求
  • 在不明確指定接收者的狀況下,向多個對象中的提交請求
  • 可動態指定一組對象處理請求

  6. 應用實例

  • 多級請求
  • 擊鼓傳花
  • 請假/加薪請求
  • Java Web中Tomcat對Encoding的處理、攔截器

  7. 注意事項

  • 需控制鏈中最大節點數量,通常經過在Handler中設置一個最大節點數量,在setNext()方法中判斷是否已經超過閥值,超過則不容許該鏈創建,避免出現超長鏈無心識地破壞系統性能

3、職責鏈模式的實現

  咱們就以請假/加薪爲例,實現一個較爲簡單的職責鏈模式。UML圖以下:

  1. 抽象管理者

  經過Manager抽象類管理全部管理者,setSuperior()方法用於定義職責鏈的下一級,即定義當前管理者的上級。

 1 public abstract class Manager {
 2     
 3     protected String name;
 4     protected Manager superior;    //管理者的上級
 5     
 6     public Manager(String name) {
 7         this.name = name;
 8     }
 9     
10     //設置管理者的上級
11     public void setSuperior(Manager superior) {
12         this.superior = superior;
13     }
14     
15     //申請請求
16     public abstract void handlerRequest(Request request);
17 
18 }

  2. 具體管理者

  經理類以下,只可批准兩天之內的假期,其他請求將繼續申請上級。

 1 public class CommonManager extends Manager {
 2 
 3     public CommonManager(String name) {
 4         super(name);
 5     }
 6 
 7     @Override
 8     public void handlerRequest(Request request) {
 9         if (request.getRequestType().equals("請假") && request.getNumber() <= 2) {    //只能批准兩天內的假期
10             System.out.println(name + ":" + request.getRequestContent() + ",時長:" + request.getNumber() + "天,被批准");
11         } else {    //其他請求申請上級
12             if (superior != null) {
13                 superior.handlerRequest(request);
14             }
15         }
16     }
17 
18 }

  總監類以下,只可批准五天之內的假期,其他請求將繼續申請上級。

 1 public class Majordomo extends Manager {
 2 
 3     public Majordomo(String name) {
 4         super(name);
 5     }
 6 
 7     @Override
 8     public void handlerRequest(Request request) {
 9         if (request.getRequestType().equals("請假") && request.getNumber() <= 5) {    //只能批准五天內的假期
10             System.out.println(name + ":" + request.getRequestContent() + ",時長:" + request.getNumber() + "天,被批准");
11         } else {    //其他請求申請上級
12             if (superior != null) {
13                 superior.handlerRequest(request);
14             }
15         }        
16     }
17 
18 }

  總經理類,能夠批准任意時常的假期,而且能夠批准是否加薪。

 1 public class GeneralManager extends Manager {
 2 
 3     public GeneralManager(String name) {
 4         super(name);
 5     }
 6 
 7     @Override
 8     public void handlerRequest(Request request) {
 9         if (request.getRequestType().equals("請假")) {    //能批准任意時長的假期
10             System.out.println(name + ":" + request.getRequestContent() + ",時長:" + request.getNumber() + "天,被批准");
11         } else if (request.getRequestType().equals("加薪") && request.getNumber() <= 500) {
12             System.out.println(name + ":" + request.getRequestContent() + ",金額:¥" + request.getNumber() + ",被批准");
13         } else if (request.getRequestType().equals("加薪") && request.getNumber() > 500) {
14             System.out.println(name + ":" + request.getRequestContent() + ",金額:¥" + request.getNumber() + ",再說吧");
15         }
16     }
17 
18 }

  3. 申請類

 1 public class Request {
 2     
 3     private String requestType;    //申請類別
 4     private String requestContent;    //申請內容
 5     private int number;    //數量
 6 
 7     public String getRequestType() {
 8         return requestType;
 9     }
10 
11     public void setRequestType(String requestType) {
12         this.requestType = requestType;
13     }
14 
15     public String getRequestContent() {
16         return requestContent;
17     }
18 
19     public void setRequestContent(String requestContent) {
20         this.requestContent = requestContent;
21     }
22 
23     public int getNumber() {
24         return number;
25     }
26 
27     public void setNumber(int number) {
28         this.number = number;
29     }
30     
31 }

   4. Client客戶端

  下面測試幾組數據。

 1 public class Client {
 2     
 3     public static void main(String[] args) {
 4         CommonManager commonManager = new CommonManager("尼古拉斯·經理");
 5         Majordomo majordomo = new Majordomo("尼古拉斯·總監");
 6         GeneralManager generalManager = new GeneralManager("尼古拉斯·總經理");
 7         
 8         //設置上級
 9         commonManager.setSuperior(majordomo);
10         majordomo.setSuperior(generalManager);
11         
12         Request request = new Request();
13         request.setRequestType("請假");
14         request.setRequestContent("adam請假");
15         request.setNumber(1);
16         commonManager.handlerRequest(request);
17         
18         Request request2 = new Request();
19         request2.setRequestType("請假");
20         request2.setRequestContent("adam請假");
21         request2.setNumber(4);
22         commonManager.handlerRequest(request2);
23         
24         Request request3 = new Request();
25         request3.setRequestType("加薪");
26         request3.setRequestContent("adam請求加薪");
27         request3.setNumber(500);
28         commonManager.handlerRequest(request3);
29         
30         Request request4 = new Request();
31         request4.setRequestType("加薪");
32         request4.setRequestContent("adam請求加薪");
33         request4.setNumber(1000);
34         commonManager.handlerRequest(request4);
35     }
36 
37 }

   運行結果以下:

  

 

  源碼地址:https://gitee.com/adamjiangwh/GoF 

相關文章
相關標籤/搜索