1. 程式人生 > >設計模式是什麼鬼(責任鏈)

設計模式是什麼鬼(責任鏈)

  • //本文作者:凸凹裡歐
  • //本文收錄選單欄:《設計模式是什麼鬼》專欄中

曾經有這麼一些零散的功能節點,他們各自承擔各自的義務,分工明確,各司其職。為了更高效,更完整地解決客戶的問題,他們發揚團隊精神,互相串聯起來形成一個有序的責任傳遞連結串列,於是責任鏈模式誕生了。當然,它的結構也不一定非得是連結串列,甚至可以是樹型分叉結構,這要根據業務場景看怎樣去靈活運用,但其核心意義是為了處理某種連續的流程,並確保業務一定能走到相應的責任節點上並得到相應的處理。

說到這裡想必大家已經想到了工作流吧?對,企事業單位中通常為了完成某項日常任務,通常要制定一些工作流程,按步驟拆分,並組織好各個環節中的邏輯關係及走向,這樣才能更高效、更規範地完成任務。

根據以上流程圖,我們來做一個最簡單的例子。假設某公司針對出差報銷業務制定審批流程,有三個審批角色分別是員工(1000元許可權)、經理(5000元許可權)、以及CEO(10000元許可權),各審批人程式碼如下。

public class Staff {

    private String name;

    public Staff(String name) {
        this.name = name;
    }

    public boolean approve(int amount) {
        if (amount <= 1000) {
            System.out.println(
"審批通過。【員工:" + name + "】"); return true; } else { System.out.println("無權審批,請找上級。【員工:" + name + "】"); return false; } } }
public class Manager {

    private String name;

    public Manager(String name) {
        this.name = name;
    }

    
public boolean approve(int amount) { if (amount <= 5000) { System.out.println("審批通過。【經理:" + name + "】"); return true; } else { System.out.println("無權審批,請找上級。【經理:" + name + "】"); return false; } } }
public class CEO {

    private String name;

    public CEO(String name) {
        this.name = name;
    }

    public boolean approve(int amount) {
        if (amount <= 10000) {
            System.out.println("審批通過。【CEO:" + name + "】");
            return true;
        } else {
            System.out.println("駁回申請。【CEO:" + name + "】");
            return false;
        }
    }

}

好了,審批人們定義完畢,邏輯非常簡單縝密,如果超過審批金額最大許可權則打回去,開始寫申請人客戶端類。

public class Client {
    public static void main(String[] args) {
        int amount = 10000;//出差花費10000元
        // 先找員工張飛審批
        Staff staff = new Staff("張飛");
        if (!staff.approve(amount)) {
            //被拒,找關二爺問問。
            Manager manager = new Manager("關羽");
            if (!manager.approve(amount)) {
                //還是被拒,只能找老大了。
                CEO ceo = new CEO("劉備");
                ceo.approve(amount);
            }
        }
        /***********************
        無權審批,請找上級。【員工:張飛】
        無權審批,請找上級。【經理:關羽】
        審批通過。【CEO:劉備】
        ***********************/
    }
}

功夫不負有心人,跑了三個地方找了三個人,一萬元的大額報銷單終於被大老闆審批了。然而,大家有沒有發現問題?我們走的審批流程好像有點過於複雜了,找這個不行那個不同意,跑來跑去的好像自己有點像是被踢皮球的感覺。此外,如果我們後期要優化完善此工作流程,或是新增新的審批角色進來,那就得不停地修改此處的邏輯,最終的修改結果會是?

亂了,全亂套了,我們終將被淹沒在一堆複雜的審批流程中,跑斷腿也找不到門路。這顯然是違反設計模式原則的,我們必須進行重構。我們觀察此類中的審批邏輯,這顯然就是一個鏈式結構,審批人之間環環相扣,對於自己無法處理的申請,會像被踢皮球似的傳給上級,直到某人解決此申請,對員工張飛來說,他只知道自己傳球給關羽了,僅此而已。

進一步分析,審批人肯定是不同的角色,並且每個角色的審批邏輯會有區別,所以我們得把這些角色的審批邏輯分開來寫,對每個角色的責任範圍我們進行定義,我只懂自己怎麼審批(責任),我處理不了的我遞交給上層(鏈條),開始重構,先抽象出一個審批人類。

public abstract class Approver {// 審批人抽象類

    protected String name;// 抽象出審批人的姓名。
    protected Approver nextApprover;// 下一個審批人,更高級別領導。

    public Approver(String name) {
        this.name = name;
    }

    protected Approver setNextApprover(Approver nextApprover) {
        this.nextApprover = nextApprover;
        return this.nextApprover;// 返回下個審批人,鏈式程式設計。
    }

    public abstract void approve(int amount);// 抽象審批方法由具體審批人子類實現
}

注意第4行,審批人只認識自己的領導,所以會持有下一級領導的引用,同時第10行的程式碼用於把領導注入進來。第15行是我們的審批方法了,但每個角色審批邏輯會有區別,所以這裡進行抽象,並由具體的審批角色子類去實現,先從員工看起。

public class Staff extends Approver {

    public Staff(String name) {
        super(name);
    }

    @Override
    public void approve(int amount) {
        if (amount <= 1000) {
            System.out.println("審批通過。【員工:" + name + "】");
        } else {
            System.out.println("無權審批,升級處理。【員工:" + name + "】");
            this.nextApprover.approve(amount);
        }
    }

}

很簡單,員工類繼承了審批角色類,第9行申明審批許可權為1000元,重點在於第13行這裡呼叫了自己上級領導的審批方法,顯然這裡是自己處理不了的申請單了。大同小異,再重構經理及CEO審批角色類。

public class Manager extends Approver {

    public Manager(String name) {
        super(name);
    }

    @Override
    public void approve(int amount) {
        if (amount <= 5000) {
            System.out.println("審批通過。【經理:" + name + "】");
        } else {
            System.out.println("無權審批,升級處理。【經理:" + name + "】");
            this.nextApprover.approve(amount);
        }
    }

}
public class CEO extends Approver {

    public CEO(String name) {
        super(name);
    }

    @Override
    public void approve(int amount) {
        if (amount <= 10000) {
            System.out.println("審批通過。【CEO:" + name + "】");
        } else {
            System.out.println("駁回申請。【CEO:" + name + "】");
        }
    }

}

CEO類作為鏈條的尾巴,也就是最高級別,第12行的越權邏輯會最終拒絕申請單。很簡單吧?我們生成一下這個鏈條,並從員工開始傳遞申請單。

public class Client {
    public static void main(String[] args) {
        Approver flightJohn = new Staff("張飛");
        flightJohn.setNextApprover(new Manager("關羽")).setNextApprover(new CEO("劉備"));

        //高層接觸不到也沒必要接觸,直接找員工張飛審批。
        flightJohn.approve(1000);
        /***********************
        審批通過。【員工:張飛】
        ***********************/

        flightJohn.approve(4000);
        /***********************
        無權審批,升級處理。【員工:張飛】
        審批通過。【經理:關羽】
        ***********************/

        flightJohn.approve(9000);
        /***********************
        無權審批,升級處理。【員工:張飛】
        無權審批,升級處理。【經理:關羽】
        審批通過。【CEO:劉備】
        ***********************/

        flightJohn.approve(88000);
        /***********************
        無權審批,升級處理。【員工:張飛】
        無權審批,升級處理。【經理:關羽】
        駁回申請。【CEO:劉備】
        ***********************/
    }
}

這裡注意第4行的程式碼對責任鏈進行構造(其實這裡我們還可以交由工作流工廠去構造完成,讀者可以自己實踐練習),從員工開始一直到CEO結束。之後的業務就非常簡單了,直接遞單給員工張飛,審批流程便魔法般地啟動了,審批單在這個責任鏈條上層層遞交,最終給出結果。

至此,申請人與審批人實現瞭解耦,我們只需遞單送給責任鏈即可,申請人不必再關心每個處理細節,只需交給介面人張飛處理就妥了。使用了責任鏈模式後的程式碼看起來非常簡潔,各個角色的責任劃分非常明確並且被分開定義到了每個角色類中,再把他們串起來去呼叫,一氣呵成。後期如果再繼續新增新的角色只需要新增新角色類並加入鏈條即可,鏈條的隨意伸縮,靈活的可伸縮性,完美的可擴充套件性。掛上這條鈦合金項鍊,維護世界和平的責任就交給你了!

在實際應用中,我們切勿生搬硬套,還需根據實際需求場景進行靈活運用,就拿工業現代化生產線舉例,這個其實也類似責任鏈模式,但不同之處在於其組裝工作是必須經過每個組裝節點處理的,從頭到尾的全鏈處理而不能中途退出,讀者朋友可以自己寫程式碼練習,實踐與思考要相結合並迴圈往復,二者都非常重要。