017.責任鏈模式

中國古代對婦女制定了“三從四德”的道德規(guī)范,“三從”是指“未嫁從父、既嫁從夫、夫死從子”,也就是說一個女性,在沒有結婚的時候要聽從于父親,結了婚后聽從于丈夫,丈夫死了還要聽兒子的,那我們來看怎么把“三從”通過我們的程序來實現(xiàn),需求很簡單:通過程序描述一下古代婦女的“三從”制度,好我們先看類圖:

代碼如下:

/**
 * 古代悲哀女性的總稱
 */
public interface IWomen {

    /**
     * 返回個人狀況,已婚、未婚、是否喪偶
     * @return 個人狀況
     */
    int getType();

    /** 獲取個人請示:要干什么?逛街?看電影?
     * @return 個人請示
     */
    String getRequest();

}

public class Women implements IWomen {

    /**
     * 1: 未婚
     * 2: 已婚
     * 3: 喪偶
     */
    private int type = 0;

    /**
     * 婦女的請示
     */
    private String request = "";

    public Women(int type, String request) {
        this.type = type;
        this.request = request;
    }

    @Override
    public int getType() {
        return type;
    }

    @Override
    public String getRequest() {
        return request;
    }
}

/**
 * 父系社會,那就是男性有至高權利,handler控制權
 */
public interface IHandler {

    /**
     * 一個女性(女兒,妻子或者是母親)要求逛街,你要處理這個請求
     * @param women 一個女性
     */
    void handleMessage(IWomen women);

}

public class Father implements IHandler {

    /**
     * 未出嫁女兒來請示父親
     */
    @Override
    public void handleMessage(IWomen women) {
        System.out.println("女兒的請示是: " + women.getRequest());
        System.out.println("父親的答復是: 同意");
    }
}

public class Husband implements IHandler {

    /**
     * 妻子向丈夫請示
     */
    @Override
    public void handleMessage(IWomen women) {
        System.out.println("妻子的請示是: " + women.getRequest());
        System.out.println("丈夫的答復是: 同意");
    }
}

public class Son implements IHandler {

    /**
     * 母親向兒子請示
     */
    @Override
    public void handleMessage(IWomen women) {
        System.out.println("母親的請示是: " + women.getRequest());
        System.out.println("兒子的答復是: 同意");
    }
}

public class Client {

    public static void main(String[] args) {

        Random random = new Random();
        ArrayList<IWomen> arrayList = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            arrayList.add(new Women(random.nextInt(3) + 1, "我要出去逛街"));
        }

        IHandler father = new Father();
        IHandler husband = new Husband();
        IHandler son = new Son();

        for (IWomen iWomen : arrayList) {
            if (iWomen.getType() == 1) {
                System.out.println("----------女兒向父親請示----------");
                father.handleMessage(iWomen);
            } else if (iWomen.getType() == 2) {
                System.out.println("----------妻子向丈夫請示----------");
                husband.handleMessage(iWomen);
            } else if (iWomen.getType() == 3) {
                System.out.println("----------母親向兒子請示----------");
                son.handleMessage(iWomen);
            }
        }

    }

}

我們上面的代碼有以下幾個問題:

  • 失去面向對象的意義。對女兒提出的請示,應該在父親類中做出決定,父親這個類應該是知道女兒的請求應該自己處理,而不是在 Client類中進行組裝出來,也就是說原本應該是父親這個類做的事情拋給了其他類進行處理;
  • 與迪米特法則相違背。我們在Client類中寫了if else的判斷條件,這個條件體內都是一個接口IHandler的三個實現(xiàn)類,誰能處理那個請求,怎么處理,直接在實現(xiàn)類中定義好不就結了嗎?你的類我知道的越少越好,別讓我猜測你類中的邏輯,想想看,把這段 if else移動到三個實現(xiàn)類中該怎么做?
  • 耦合過重。這個什么意思呢,我們要根據(jù)Womentype來決定使用IHandler的哪個實現(xiàn)類來處理請求,我問你,如果IHanlder的實現(xiàn)類繼續(xù)擴展怎么辦?修改Client類?與開閉原則違背嘍!
  • 異常情況沒有考慮。妻子只能向丈夫請示嗎?如果妻子向自己的父親請示了,父親應該做何處理?我們的程序上可沒有體現(xiàn)出來。

既然有這么多的問題,那我們要想辦法來解決這些問題,我們可以抽象成這樣一個結構,女性的請求先發(fā)送到父親類,父親類一看是自己要處理的,就回應處理,如果女兒已經(jīng)出嫁了,那就要把這個請求轉發(fā)到女性的丈夫來處理,如果女性喪偶了,那就由兒子來處理這個請求,類似于這樣請求:

父親、丈夫、兒子每個節(jié)點有兩個選擇:要么承擔責任,做出回復;要么把請求轉發(fā)到后序環(huán)節(jié)。結構分析的已經(jīng)很清楚了,那我們看怎么來實現(xiàn)這個功能,先看類圖:

從類圖上看,三個實現(xiàn)類Father、HusbandSon只要實現(xiàn)構造函數(shù)和父類中的抽象方法就可以了,具體怎么處理這些請求,都已經(jīng)轉移到了Hanlder抽象類中,我們來看Hanlder怎么實現(xiàn):

public abstract class Handler {

    // 能處理的級別
    private int level = 0;

    // 責任傳遞,下一個人責任人是誰
    private Handler nextHandler;

    public Handler(int level) {
        this.level = level;
    }

    public void setNext(Handler handler) {
        this.nextHandler = handler;
    }

    public abstract void response(IWomen women);

    public final void handleMessage(IWomen women) {
        if (women.getType() == level) {
            response(women);
        } else {
            if (nextHandler != null) {
                nextHandler.handleMessage(women);
            } else {
                System.out.println("----------沒有地方請示了,不做處理----------");
            }
        }
    }

}

有沒有看到,其實在這里也用到模版方法模式,在模版方法中判斷請求的級別和當前能夠處理的級別,如果相同則調用基本方法,做出反饋;如果不相等,則傳遞到下一個環(huán)節(jié),由下一環(huán)節(jié)做出回應。基本方法response要各個實現(xiàn)類都要實現(xiàn),我們來看三個實現(xiàn)類:

public interface IWomen {

    /**
     * 返回個人狀況,已婚、未婚、是否喪偶
     * @return 個人狀況
     */
    int getType();

    /** 獲取個人請示:要干什么?逛街?看電影?
     * @return 個人請示
     */
    String getRequest();

}

public class Women implements IWomen {

    /**
     * 1: 未婚
     * 2: 已婚
     * 3: 喪偶
     */
    private int type = 0;

    /**
     * 婦女的請示
     */
    private String request = "";

    public Women(int type, String request) {
        this.type = type;
        switch (this.type) {
            case 1:
                this.request = "女兒的請求是: " + request;
                break;
            case 2:
                this.request = "妻子的請求是: " + request;
                break;
            case 3:
                this.request = "母親的請求是: " + request;
                break;
            default: // do nothing
        }
    }

    @Override
    public int getType() {
        return type;
    }

    @Override
    public String getRequest() {
        return request;
    }
}

public abstract class Handler {

    // 能處理的級別
    private int level = 0;

    // 責任傳遞,下一個人責任人是誰
    private Handler nextHandler;

    public Handler(int level) {
        this.level = level;
    }

    public void setNext(Handler handler) {
        this.nextHandler = handler;
    }

    public abstract void response(IWomen women);

    public final void handleMessage(IWomen women) {
        if (women.getType() == level) {
            response(women);
        } else {
            if (nextHandler != null) {
                nextHandler.handleMessage(women);
            } else {
                System.out.println("----------沒有地方請示了,不做處理----------");
            }
        }
    }

}

public class Father extends Handler {

    public Father() {
        super(1);
    }

    // 父親的答復
    @Override
    public void response(IWomen women) {
        System.out.println("--------女兒向父親請示-------");
        System.out.println(women.getRequest());
        System.out.println("父親的答復是: 同意");
    }
}

public class Husband extends Handler {

    public Husband() {
        super(2);
    }

    @Override
    public void response(IWomen women) {
        System.out.println("--------妻子向丈夫請示-------");
        System.out.println(women.getRequest());
        System.out.println("丈夫的答復是: 同意");
    }
}

public class Son extends Handler {

    public Son() {
        super(3);
    }

    @Override
    public void response(IWomen women) {
        System.out.println("--------母親向兒子請示-------");
        System.out.println(women.getRequest());
        System.out.println("兒子的答復是: 同意");
    }

}

public class Client {

    public static void main(String[] args) {

        Random random = new Random();
        ArrayList<IWomen> arrayList = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            arrayList.add(new Women(random.nextInt(3) + 1, "我要出去逛街"));
        }

        Handler father = new Father();
        Handler husband = new Husband();
        Handler son = new Son();

        father.setNext(husband);
        husband.setNext(son);

        for (IWomen iWomen : arrayList) {
            father.handleMessage(iWomen);
        }
    }

}

通過在Client中設置請求的傳遞順序,解決了請求到底誰來回應的問題。結果也正確,業(yè)務調用類Client也不用去做判斷到底是需要誰去處理,而且Handler抽象類的子類以后可以繼續(xù)增加下去,只是我們這個傳遞鏈增加而已,調用類可以不用了解變化過程,甚至是誰在處理這個請求都不用知道。

以上講解的就是責任鏈模式,你看Father、Husband、Son 這三個類的處理女性的請求時是不是在傳遞呀,每個環(huán)節(jié)只有兩個選項:要么承擔責任做出回應,要么向下傳遞請求,最終會有環(huán)節(jié)做出回應,通用類圖如下:

在通用類圖中Handler是一個接口或者是抽象類,每個實現(xiàn)類都有兩個方法handlerRequest()是處理請求,setNext()是設置當前環(huán)節(jié)怎么把不屬于自己處理的請求扔給誰,對于這個類圖我覺得需要改變,融合進來模版方法模式,類圖如下:

想想單一職責法則和迪米特法則吧,通過融合模版方法模式,各個實現(xiàn)類只要關注的自己業(yè)務邏輯就成了,至于說什么事要自己處理,那就讓父類去決定好了,也就是說父類實現(xiàn)了請求傳遞的功能,子類實現(xiàn)請求的處理,符合單一職責法則,各個類只作一個動作或邏輯,也就是只有一個原因引起類的改變,我建議大家在使用的時候用這種方法,好處是非常明顯的了,子類的實現(xiàn)非常簡單,責任鏈的建立也非常的靈活。

責任鏈模式屏蔽了請求的處理過程,你發(fā)起一個請求到底是誰處理的,這個你不用關心,只要你把請求拋給責任鏈的第一個處理者,最終會返回一個處理結果(當然也可以不做任何處理),作為請求者可以不用知道到底是需要誰來處理的,這是責任鏈模式的核心;同時責任鏈模式也可以做為一種補救模式來使用,舉個簡單例子,如項目開發(fā)的時候,需求確認是這樣的:一個請求(比如銀行客戶存款的幣種),一個處理者(只處理人民幣),但是隨著業(yè)務的發(fā)展(改革開放了嘛,還要處理美元、日元等等),處理者的數(shù)量和類型都有所增加,那這時候就可以在第一個處理者后面建立一個鏈,也就是責任鏈來處理請求,你是人民幣,好,還是第一個業(yè)務邏輯來處理,你是美元,好,傳遞到第二個業(yè)務邏輯來處理,日元,歐元…,這些都不用在對原有的業(yè)務邏輯產(chǎn)生很大改變,通過擴展實現(xiàn)類就可以很好的解決這些需求變更的問題。

責任鏈有一個缺點是大家在開發(fā)的時候要注意:調試不是很方便,特別是鏈條比較長,環(huán)節(jié)比較多的時候,由于采用了類似遞歸的方式,調試的時候邏輯可能比較復雜。

觀察者模式也可以實現(xiàn)請求的傳遞,比如一個事件發(fā)生了,通知了觀察者,同時觀察者又作為一個被觀察者,通知了另外一個觀察者,這也形成了一個事件廣播鏈,這和我們今天講的責任鏈是有區(qū)別的:

  • 受眾數(shù)量不同。觀察者廣播鏈式可以 1:N 的方式廣播,而責任鏈則要求是的 1:1 的傳遞,必然有一個且只有一個類完成請求的處理;
  • 請求內容不同。觀察者廣播鏈中的信息可以在傳播中改變,但是責任鏈中的請求是不可改變的;
  • 處理邏輯不通。觀察者廣播鏈主要用于觸發(fā)聯(lián)動動作,而責任鏈則是對一個類型的請求按照既定的規(guī)則進行處理。

本文原書:

《您的設計模式》 作者:CBF4LIFE

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內容