設計模式學習筆記(十六):職責鏈模式

1 概述

1.1 引言

不少狀況下,一個軟件系統中能夠處理某個請求的對象不知一個,好比採購單的審批,主任,副董事長,董事長,董事會均可以處理採購單,他們能夠構成一條處理採購單的鏈式結構,採購單沿着這條鏈進行傳遞,這條鏈就叫職責鏈。java

職責鏈能夠是一條直線,一個環或者一個樹形結構,最多見的職責鏈是直線型,即沿着一條單向的鏈來傳遞請求。鏈上的每個對象都是請求處理者,職責鏈模式能夠將請求的處理者組織成一條鏈,並讓請求沿着鏈傳遞,由鏈上的處理者對請求進行處理,客戶端無須關係請求的處理細節以及具體的傳遞,只須要將請求發送到鏈上便可,實現請求發送者以及請求處理者的解耦。編程

1.2 定義

職責鏈模式:避免將請求發送者與接收者耦合在一塊兒,讓多個對象都有機會接收請求,將這些對象鏈接成一條鏈,而且沿着這條鏈傳遞請求,直到有對象處理它爲止。微信

職責鏈模式是一種行爲型模式。dom

1.3 結構圖

在這裏插入圖片描述

1.4 角色

  • Handler(抽象處理者):定義一個處理請求的接口,通常爲抽象類。由於每個處理者的下家仍是一個處理者,所以在抽象處理者中定義了一個抽象處理者的對象做爲對下一個處理者的引用,經過該引用,處理者能夠連成一條鏈
  • ConcreteHandler(具體處理者):抽象處理者的子類,實現具體處理方法,在處理前須要判斷是否具備處理權限,若是擁有權限則處理,沒有則轉發到下一個處理者

2 典型實現

2.1 步驟

  • 定義抽象處理者:定義處理請求接口以及定義一個抽象處理者成員,做爲下一個處理者的引用,通常爲了讓具體處理者方便調用,定義爲protected
  • 定義具體處理者:處理/轉發請求,處理請求前先判斷是否具備權限 ,擁有則處理請求,不然轉發請求
  • 客戶端建立職責鏈:職責鏈模式並不建立職責鏈,職責鏈交由客戶端建立,根據實際須要定義職責鏈順序

2.2 抽象處理者

abstract class Handler
{
    protected Handler successor;
    public void setSuccessor(Handler successor)
    {
        this.successor = successor;
    }

    public abstract void handleRequest(int num);
}

擁有一個設置下一處理者的對象,能夠經過setter注入,同時聲明抽象處理方法。ide

2.3 具體處理者

class ConcreteHandler1 extends Handler
{
    @Override    
    public void handleRequest(int num)
    {
        if(num < 10)
        {
            System.out.println("處理小於10的數字:"+num);
        }
        else
            successor.handleRequest(num);
    }
}

class ConcreteHandler2 extends Handler
{
    @Override    
    public void handleRequest(int num)
    {
        if(num < 20)
        {
            System.out.println("處理大於等於10且小於20的數字:"+num);
        }
        else
            successor.handleRequest(num);
    }
}

class ConcreteHandler3 extends Handler
{
    @Override    
    public void handleRequest(int num)
    {
        if(num < 30)
        {
            System.out.println("處理大於等於20且小於30的數字:"+num);
        }
        else
            successor.handleRequest(num);
    }
}

繼承抽象處理者,首先判斷是否擁有權限處理(這裏是一個簡單的if判斷),若是有就處理,沒有的話經過protected對象,也就是轉發給下一個處理者處理。性能

2.4 客戶端

public static void main(String[] args) 
{
    Handler handler = new ConcreteHandler1();
    Handler handler2 = new ConcreteHandler2();
    Handler handler3 = new ConcreteHandler3();
    handler.setSuccessor(handler2);
    handler2.setSuccessor(handler3);

    handler.handleRequest(3);
    handler.handleRequest(15);
    handler.handleRequest(22);
}

客戶端針對抽象處理者編程,須要建立每個具體處理者對象,而且自定義職責鏈:測試

handler.setSuccessor(handler2);
handler2.setSuccessor(handler3);

接着調用對應的處理者處理便可。this

3 實例

設計一個採購單審批系統,分級進行,根據金額不一樣由不一樣層次的人員審批,主任能夠審批5w如下的採購單,副董事長能夠審批5w-10w,董事長能夠審批10w-50w,50w以上須要由董事會審批,使用職責鏈模式設計該系統。

設計以下:spa

  • 抽象處理者:Approver
  • 具體處理者:Director+VicePresident+President+Congress
  • 採購單請求類:PurchaseRequest

代碼以下:設計

//抽象處理者
abstract class Approver
{
    protected Approver successor;

    public void setSuccessor(Approver successor) {
        this.successor = successor;
    }

    public abstract void processRequest(PurchaseRequest request);
}

//具體處理者:主任
class Director extends Approver
{
    @Override
    public void processRequest(PurchaseRequest request)
    {
        if(request.getAmount() < 50000)
            System.out.println("主任審批一筆\n金額爲"+request.getAmount()+"\nid爲"+request.getId()+"\n的採購單\n");
        else
            successor.processRequest(request);
    }
}

//具體處理者:副董事長
class VicePresident extends Approver
{
    @Override
    public void processRequest(PurchaseRequest request)
    {
        if(request.getAmount() < 100000)
            System.out.println("副董事長審批一筆\n金額爲"+request.getAmount()+"\nid爲"+request.getId()+"\n的採購單\n");
        else
            successor.processRequest(request);
    }
}

//具體處理者:董事長
class President extends Approver
{
    @Override
    public void processRequest(PurchaseRequest request)
    {
        if(request.getAmount() < 500000)
            System.out.println("董事長審批一筆\n金額爲"+request.getAmount()+"\nid爲"+request.getId()+"\n的採購單\n");
        else
            successor.processRequest(request);
    }
}

//具體處理者:董事會
class Congress extends Approver
{
    @Override
    public void processRequest(PurchaseRequest request)
    {
        System.out.println("董事會審批一筆\n金額爲"+request.getAmount()+"\nid爲"+request.getId()+"\n的採購單\n");
    }
}

//請求類:採購單
class PurchaseRequest
{
    private double amount;
    private String id;
    private static final String STR = "xcnvj232cvm";
    private static final Random random = new Random();

    public PurchaseRequest(double amount)
    {
        this.amount = amount;
        //簡易的隨機字符串
        this.id = STR.substring(0,random.nextInt(STR.length()-1)+1).repeat(random.nextInt(3)+2);
    }

    public double getAmount() {
        return this.amount;
    }

    public void setAmount(double amount) {
        this.amount = amount;
    }

    public String getId() {
        return this.id;
    }

    public void setId(String id) {
        this.id = id;
    }

}

測試:

public static void main(String[] args) 
{
    Approver director = new Director();
    Approver vicePresident = new VicePresident();
    Approver president = new President();
    Approver congress = new Congress();

    director.setSuccessor(vicePresident);
    vicePresident.setSuccessor(president);
    president.setSuccessor(congress);

    director.processRequest(new PurchaseRequest(12345));
    director.processRequest(new PurchaseRequest(54321));
    director.processRequest(new PurchaseRequest(123456));
    director.processRequest(new PurchaseRequest(654321));
}

輸出以下:
在這裏插入圖片描述

4 分類

職責鏈模式能夠分爲純的職責鏈模式與不純的職責鏈模式。

4.1 純的職責鏈模式

一個純的職責鏈模式要求一個具體處理者對象只能在兩個行爲中選擇一個,要麼承擔所有責任,要麼將責任推給下家,不容許出現某一個具體處理者對象在承擔了一部分或所有責任後又將責任向下傳遞的狀況。

並且在純的職責鏈模式中,要求一個請求必須被某一個處理者對象接收,不能出現某個請求未被任何一個處理者對象處理的狀況,好比前面的採購單例子。

4.2 不純的職責鏈模式

在一個不純的職責鏈模式中,容許某個請求被一個具體處理者部分處理後再向下傳遞,或者一個具體處理者處理完某請求後其猴戲處理者能夠繼續處理該請求,並且一個請求能夠最終不被任何處理者對象所接收。

在Java AWT 1.0中的事件處理模型應用就是不純的職責鏈模式,基本原理以下:因爲窗口組件通常位於容器組件中,當事件發生在窗口組件上時,先經過組件對象的handleEvent()方法傳遞給相應的事件處理方法,該事件處理方法將處理該事件,而後決定是否將該事件向上一級容器組件傳播,上級容器組件在接到事件以後能夠繼續處理此事件並決定是否繼續向上級容器組件傳播,直到頂層容器組件爲止。若是一直都沒有處理方法則不處理該事件。

這種事件處理機制又叫事件浮升機制,JDK1.1後使用觀察者模式來代理職責鏈模式處理事件。

5 主要優勢

  • 下降耦合:職責鏈模式使得一個對象無須知道是其餘哪個對象處理請求,對象僅需知道請求會被處理便可,接收者和發送者都沒有對方明確信息,且鏈中對象不須要知道鏈的結構,由客戶端負責鏈的建立,下降了系統耦合度
  • 簡化對象鏈接:請求處理對象僅需維持一個指向其後繼者的引用,而不須要維持它對全部候選處理者的引用,可簡化對象的相互鏈接
  • 靈活的職責鏈:能夠在運行時對鏈進行動態增長或者修改
  • 符合OCP:系統增長一個新的具體處理者時無須修改源碼,只須要客戶端重建職責鏈,符合OCP

6 主要缺點

  • 請求可能得不處處理:因爲一個請求沒有明確的接收者,所以請求不必定會被處理,也有可能由於職責鏈配置錯誤而得不處處理
  • 性能受到影響:對於較長的職責鏈,請求的處理可能涉及多個處理對象,系統性能會受到必定影響,並且代碼調試時可能不方便
  • 死循環:若是職責鏈不當,可能會致使死循環調用

7 適用場景

  • 有多個對象能夠處理同一個請求,具體哪一個對象處理該請求待運行時刻再肯定,客戶端只需將請求提交到鏈上,而無須關心請求的處理對象是誰以及它是如何處理的
  • 在不明確指定接收者的狀況下,向多個對象的一個提交一個請求
  • 可動態指定一組對象處理請求,客戶端能夠動態建立職責鏈來處理請求,還能夠改變鏈中處理請求以及處理者之間的前後次序

8 總結

在這裏插入圖片描述

若是以爲文章好看,歡迎點贊。

同時歡迎關注微信公衆號:氷泠之路。

在這裏插入圖片描述

相關文章
相關標籤/搜索