責任鏈模式淺析

  • 2019 年 10 月 4 日
  • 筆記

在開始本文之前,我們先來看下生活中的的一些場景:

  • 場景1:請假

某天你想請個假,比如1到3天,直接主管可以審批;3天以上需要部門主管審批;15天以上需要副總裁審批 … …

  • 場景2:打折

某天你去買個車,和銷售人員討論折扣的問題。這個時候,可能95折以上銷售能拍板;85折需要銷售經理拍板;75折需要區域經理拍板 … …

這些場景,當某一個人或者節點不能處理的時候,需要拋給下一個節點處理。這就是我們今天要聊的責任鏈模式可以做的事情。

一、責任鏈模式的基本介紹

1.1 意圖

責任鏈的意圖是使多個對象都有機會處理請求,從而避免請求的發送者和接收者之前的耦合關係。將這些對象連成一條鏈,並沿著這條鏈傳遞該請求,直到有一個對象處理它為止。

1.2 結構

責任鏈模式的基本結構如下:

  • Handler
    • 定義一個處理請求的介面。
  • ConcreteHandler
    • 處理它所負責的請求
    • 可訪問它的後繼者

二、責任鏈模式示例

本責任鏈模式示例模擬買東西打折扣,銷售人員能處理9折以上的折扣;如果折扣低於9折向銷售經理請示;同理,銷售經理能處理8折以上的折扣,如果折扣低於8折,其需要向區域經理請示;區域經理能處理7折以上的折扣。

  • DiscountHandler (Handler)

折扣處理類,其包含一個折扣處理方法handle, 還有一個後繼處理者 successor。

package com.wangmengjun.tutorial.designpattern.responsibility;    public abstract class DiscountHandler {      private DiscountHandler successor;      public abstract void handle(double discount);      /**     * @return the successor     */    public DiscountHandler getSuccessor() {      return successor;    }      /**     * @param successor the successor to set     */    public void setSuccessor(DiscountHandler successor) {      this.successor = successor;    }    }  
  • 3個ConcreteHandler

定義三個具體的處理者類,分別對應銷售人員、銷售經理以及區域經理的處理,程式碼如下:

package com.wangmengjun.tutorial.designpattern.responsibility;    public class SalemanDiscountHandler extends DiscountHandler {      @Override    public void handle(double discount) {      if(discount > 0.9) {        System.out.println("Saleman Discount ==>  " + discount);      }else {        this.getSuccessor().handle(discount);      }      }    }  
package com.wangmengjun.tutorial.designpattern.responsibility;    public class SaleManagerDiscountHandler extends DiscountHandler {      @Override    public void handle(double discount) {      if(discount > 0.8) {        System.out.println("SaleManager Discount ==>  " + discount);      }else {        this.getSuccessor().handle(discount);      }      }    }  
  package com.wangmengjun.tutorial.designpattern.responsibility;    public class AreaManagerDiscountHandler extends DiscountHandler {        @Override      public void handle(double discount) {        if(discount > 0.7) {          System.out.println("AreaManager Discount ==>  " + discount);        }else {          System.out.println("折扣太低,真不能賣了~~");        }        }    }

模擬不同的折扣測試一下

package com.wangmengjun.tutorial.designpattern.responsibility;    public class ChainMain {      public static void main(String[] args) {        DiscountHandler salemanHandler = new SalemanDiscountHandler();      DiscountHandler saleManagerHandler = new SaleManagerDiscountHandler();      DiscountHandler areaManagerHandler = new AreaManagerDiscountHandler();        salemanHandler.setSuccessor(saleManagerHandler);      saleManagerHandler.setSuccessor(areaManagerHandler);        double discount = 0.95;      salemanHandler.handle(discount);        discount = 0.88;      salemanHandler.handle(discount);        discount = 0.75;      salemanHandler.handle(discount);          discount = 0.3;      salemanHandler.handle(discount);    }  }  

輸出:

Saleman Discount ==>  0.95  SaleManager Discount ==>  0.88  AreaManager Discount ==>  0.75  折扣太低,真不能賣了~~

三、小結

一個常見的責任鏈模式的程式碼是javax.servlet.Filter#doFilter()。我們可以編寫多個Filter,然後形成FilterChain來處理。

責任鏈模式能夠降低耦合度,使得一個對象無需知道是其他哪一個對象處理其請求。對象僅需知道該請求會被「正確」的處理。接收者和發送者都沒有對方的明確的資訊,且鏈中的對象不需要知道鏈的結構。他們僅需保持一個指向其後繼的引用,而不需保持它所有的候選接收者的引用。

當在對象中分派職責時,職責鏈給你更多的靈活性。你可以通過在運行時刻對該鏈進行動態的添加或修改來增加或者修改處理一個請求的的那些職責。

同時也帶來了一個問題,既然一個請求沒有明確的接收者,那麼就不能保證它一定會被處理。該請求可能一直在鏈的末端都得到不到處理,一個請求也可能因鏈沒有被正確配置而得不到處理。