提升代碼靈活性-責任鏈模式

模式介紹

責任鏈模式是行為設計模式之一。

首先我們從字面去理解責任鏈模式:“責任”指的是一個人應盡的義務、分內應做的事;“鏈”指的是一個個小環首尾相連,組成的長鏈條。

為什么是“鏈”?因為“鏈”的每一環都是可以拆卸的,它們雖然是環環相扣,但是哪天我不想要中間的某一環,我只需要將其去下來即可,這大大提升了代碼的靈活性。

所以責任鏈模式通俗來講,指的是一件事情,有順序地傳遞給一群人,當第一個人無法處理時,再傳遞給第二個人去做,直到有人可以解決掉這件事情為止。

模式示例

小明是一家技術公司的測試,他除了完成本職工作外,還負責給技術部的小伙伴購買零食,不過零食錢是先由小明自己墊付的,接著到月底拿著發票去找領導報銷的流程。

這個月的月底,小明算了一下,一共花費了2450元。

接著小明拿著零食的發票去找組長報銷,結果組長說這金額太大了,只有500以下我才有權限簽字,你得去找技術總監。

接著小明找到技術總監,總監說2450太多啦,只有2000以下我才有權限簽字,你得去找咱們的老板。

接著小明去到老板的辦公室,老板看了一下說非常好,技術部的同學們都非常辛苦,以后都多買一些零食,接著麻溜地就簽了字,小明順利完成了此月的零食報銷請求。

使用場景

通過上述示例,我相信大家已經有些理解責任鏈模式了。

組長、總監、老板針對小明的報銷請求,都有自己要做的事情,但是根據小明的報銷金額不同,無法第一時間就知道到底是誰來處理這個報銷請求。

這就需要小明按照一定的順序,一個個地去問,直到找到能處理此次報銷請求的人為止。

這種情況下,就非常適合使用我們今天要說的責任鏈模式

通過上面的描述,我們可以得出責任鏈模式的使用場景:

  • 一個請求需要多個對象處理,但具體由哪個對象處理需要在運行時動態判斷時;
  • 需要動態指定一組對象處理一個請求。

所含角色

責任鏈模式包含兩個主要角色:

  • 處理者抽象(Handler):抽象的處理者,聲明一個處理請求的方法,并在其中保持對下一個處理者的引用。
  • 處理者實現(HandlerImpl):處理者抽象的具體實現,對請求進行處理,如果無法處理,則通過下一個處理者的引用將其轉發下去。

具體代碼

針對上述示例和角色描述,我們來將其轉化成具體的代碼。

首先是處理者的抽象,針對上述示例,我們的抽象的請求處理方法應該為報銷:

public abstract class Leader {
    //下一個處理者
    private Leader nextLeader;

    /**
     * 處理報銷請求
     *
     * @param money 申請報銷的金額
     */
    public final void handlerRequest(int money) {
        if (money <= getSelfLimit()) {
            //當申請的金額<=自己的處理額度時,將此次請求消化
            handler(money);
        } else {
            //否則交給下一個處理者來處理
            if (nextLeader != null) {
                nextLeader.handlerRequest(money);
            }
        }

    }

    /**
     * 獲取自身的額度,由具體實現來設置
     *
     * @return 自身能處理的額度
     */
    public abstract int getSelfLimit();

    /**
     * 具體處理邏輯在這里實現
     *
     * @param money 申請報銷的金額
     */
    public abstract void handler(int money);


    public void setNextLeader(Leader nextLeader) {
        this.nextLeader = nextLeader;
    }

    public Leader getNextLeader() {
        return nextLeader;
    }
}

代碼還是有些多的,讓我們來解釋一下這個處理者抽象類:

  • nextLeader:指定了當請求無法處理時,下一級的處理者。這是責任鏈模式中,“鏈”的核心。對外暴露了set、get方法。
  • handlerRequest(int money):注意此方法不是抽象并且是final修飾的,也就是無法重寫,無法修改。它其中的邏輯是用來判斷此次請求,應該是由自己消化,還是分發給下一個處理者。當你要開始請求時,只需要調用此方法即可。為了適應我們舉的示例,這里有一個money參數。這里說一下,每一種設計模式僅僅代表著一種編程思想,具體代碼還是要看需求的,如果需求復雜,那么這里分發的邏輯也會復雜、參數也會更多,反之亦然。
  • getSelfLimit():抽象方法,指定處理者的最高處理額度。
  • handler(int money):當此次請求該由自己消化時,具體的消化邏輯在這里實現。

如果還有疑問,不要急,我們先來具體看一個處理者的實現,可能你就會恍然大悟:
組長:

public class LeaderGroup extends Leader {

    /**
     * @return 組長報銷的處理額度
     */
    @Override
    public int getSelfLimit() {
        return 500;
    }

    /**
     * 當報銷金額 <= getSelfLimit() 時,請求會到這里來消化
     *
     * @param money 申請報銷的金額
     */
    @Override
    public void handler(int money) {
        Toast.makeText(App.context, "小錢,組長報銷:" + money, Toast.LENGTH_SHORT).show();
    }
}

組長繼承了抽象的處理者,并指定了組長的報銷額度以及具體的處理邏輯。
同樣的,總監和老板也是一樣的道理:
總監:

public class LeaderGeneral extends Leader {

    /**
     * @return 總監報銷的處理額度
     */
    @Override
    public int getSelfLimit() {
        return 2000;
    }


    /**
     * 當報銷金額 <= getSelfLimit() 時,請求會到這里來消化
     *
     * @param money 申請報銷的金額
     */
    @Override
    public void handler(int money) {
        Toast.makeText(App.context, "金額挺大,總監報銷:" + money, Toast.LENGTH_SHORT).show();
    }
}

老板:

public class LeaderCEO extends Leader {

    /**
     * @return 老板報銷的處理額度
     */
    @Override
    public int getSelfLimit() {
        return Integer.MAX_VALUE;
    }


    /**
     * 當報銷金額 <= getSelfLimit() 時,請求會到這里來消化
     *
     * @param money 申請報銷的金額
     */
    @Override
    public void handler(int money) {
        Toast.makeText(App.context, "這么多錢,老板報銷:" + money, Toast.LENGTH_SHORT).show();
    }
}

至此,我們的處理者抽象,以及處理者實現都已經完成了,下面我們來具體模擬一下報銷流程:

//獲取輸入框中輸入的金額
String money = et_money.getText().toString();
if (TextUtils.isEmpty(money)) {
    Toast.makeText(this, "請輸入金額", Toast.LENGTH_SHORT).show();
    return;
}
//組長實例
Leader group = new LeaderGroup();
//總監實例
Leader general = new LeaderGeneral();
//CEO實例
Leader CEO = new LeaderCEO();
//組長的下一級是總監
group.setNextLeader(general);
//總監的下一級是CEO
general.setNextLeader(CEO);
//由組長優先處理報銷
group.handlerRequest(Integer.valueOf(money));

上面的測試代碼邏輯也很清晰:

  1. 獲取到具體的報銷金額
  2. 首先創了組長、總監、CEO的實例
  3. 按照組長 -> 總監 -> 老板 的報銷順序將其進行排序
  4. 由組長優先處理報銷請求,當組長無法處理時,會由組長實例中的下一個處理者來處理。

當我們需求發生變化,比如組長的報銷金額變成了1000、比如現在組長沒有報銷權限了等等,你會發現非常地好維護。

這里我們假設總監無法報銷了,當組長無法報銷時,直接找到CEO來報銷:

Leader group = new LeaderGroup();
Leader CEO = new LeaderCEO();

group.setNextLeader(CEO);

group.handlerRequest(Integer.valueOf(money));

我們僅僅是將組長的下一個處理者改為老板即可,就可以完美刪除掉總監的存在。

這種“鏈”式的寫法,完美解耦了請求者和處理者,提高代碼了靈活性。

總結

到這里,一個最基本的責任鏈模式就完成了。

代碼已經上傳至GitHub。

責任鏈模式的優點已經說過了。

缺點就是處理者太多的話,必定會影響性能,尤其是在一些遞歸調用中,使用時一定要注意。

感謝

《Android源碼設計模式解析與實戰》 何紅輝、關愛民 著

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容