設(shè)計(jì)模式系列之「責(zé)任鏈模式」

“長(zhǎng)安回望繡成堆购撼,山頂千門次第開。一騎紅塵妃子笑匕累,無人知是荔枝來”陵刹。杜牧的《過華清宮》流傳千古,楊貴妃喜食荔枝也成為眾所周知的事情欢嘿。楊貴妃吃的荔枝必須在采摘后的幾天內(nèi)送到衰琐,如果超過了四五天,荔枝就會(huì)腐爛际插,在古代路途遙遠(yuǎn)和交通不便成為了致命傷碘耳,為了讓楊貴妃吃到新鮮的荔枝而不勞民傷財(cái),小Y決定讓楊貴妃和現(xiàn)代物流來一個(gè)偶遇框弛。

楊貴妃:大王,臣妾想吃新鮮可口的荔枝(一陣撒嬌聲捕捂,各位各自想象)瑟枫。

唐玄宗:“ok!No趴笨”指攒。然后馬上召來路人甲慷妙,讓路人甲無論如何都要在三天之內(nèi)把荔枝送來,不然以死謝罪允悦。

朝中大臣紛紛幸災(zāi)樂禍膝擂,覺得路人甲肯定會(huì)死無葬身之地的。但是路人甲胸有成竹的領(lǐng)命而去(路人甲之所以這么信心十足隙弛,是因?yàn)槁啡思自蒙衩馗呷薡指點(diǎn)架馋,說命中必有一劫,只有按照幾千年后的現(xiàn)代做法建一套有效的物流方法方可避過此劫全闷,所以路人甲就按照神秘高人Y指點(diǎn)秘密在全國(guó)各地建造一個(gè)物流據(jù)點(diǎn)叉寂,然后通過特制的千里箭進(jìn)行通訊)

回到家中的路人甲里面把荔枝的貨物需求綁在千里箭中并射向了最近的據(jù)點(diǎn)bj倉(cāng)总珠,bj倉(cāng)的負(fù)責(zé)人收到之后屏鳍,發(fā)現(xiàn)這個(gè)倉(cāng)中沒有勘纯,然后又把這個(gè)需求通過千里箭射向了js倉(cāng),但最終在gd倉(cāng)找到了貨物钓瞭,再通過特制的飛鷂在三天內(nèi)把貨物送到楊貴妃所在的地方驳遵。

楊貴妃吃到了新鮮的荔枝之后,心情大悅山涡,路人甲也因此仕途暢通無阻超埋。

本故事終,故事純屬小Y瞎掰佳鳖,哇哈哈哈霍殴。

一、路人甲如何建立行之有效的物流系統(tǒng)

神秘高人Y把幾千年后的發(fā)達(dá)物流模式傳授給了路人甲系吩,要求在全國(guó)各大城市建立據(jù)點(diǎn)来庭。

1.粗糙期

①荔枝的接口

public interface ILiZhi {
    //得到荔枝的品種
    public int getType();
    //每個(gè)倉(cāng)庫(kù)中得到荔枝訂單的請(qǐng)求
    public String getRequest();
}

②定義荔枝的具體類

public class LiZhi implements ILiZhi {

    //荔枝的種類,1代表糯米糍 2代表桂味 3代表槐味
    private int type=0;
    //荔枝的訂單需求
    private String request;

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

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

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

③定義一個(gè)據(jù)點(diǎn)接口

public interface ILiZhiHouse {
    //處理訂單需求
    public void dealWithRequest(ILiZhi iLiZhi);
}

④bj據(jù)點(diǎn)穿挨,只有糯米糍荔枝

public class BJHouse implements ILiZhiHouse {
    @Override
    public void dealWithRequest(ILiZhi iLiZhi) {
        System.out.println("訂單需求:"+iLiZhi.getRequest());
        System.out.println("貨物已由bj據(jù)點(diǎn)火速送出");
    }
}

⑤js據(jù)點(diǎn)月弛,只有桂味荔枝

public class JSHouse implements ILiZhiHouse {
    @Override
    public void dealWithRequest(ILiZhi iLiZhi) {
    System.out.println("訂單需求:"+iLiZhi.getRequest());
    System.out.println("貨物已由js據(jù)點(diǎn)火速送出");
    }
}

⑥gd據(jù)點(diǎn),只有槐味荔枝

public class GDHouse implements ILiZhiHouse {
    @Override
    public void dealWithRequest(ILiZhi iLiZhi) {
    System.out.println("訂單需求:"+iLiZhi.getRequest());
    System.out.println("貨物已由gd據(jù)點(diǎn)火速送出");
    }
}

物流系統(tǒng)建好了科盛,現(xiàn)在就等楊貴妃下達(dá)荔枝需求了帽衙。

⑦楊貴妃一聲令下,要求三天之內(nèi)把糯米糍贞绵、桂味厉萝、槐味全部送達(dá)

public class Client {

    public static void main(String[] args) {
        //1代表糯米糍 2代表桂味 3代表槐味

        //路人甲根據(jù)楊貴妃的要求組裝需求
        List<ILiZhi> liZhis=new ArrayList<>();
        liZhis.add(new LiZhi(1,"需要糯米糍30斤"));
        liZhis.add(new LiZhi(2,"需要桂味30斤"));
        liZhis.add(new LiZhi(3,"需要槐味30斤"));

        //定義三個(gè)據(jù)點(diǎn)
        BJHouse bjHouse=new BJHouse();
        JSHouse jsHouse=new JSHouse();
        GDHouse gdHouse=new GDHouse();

        for(ILiZhi liZhi:liZhis){
            if(liZhi.getType()==1){
                bjHouse.dealWithRequest(liZhi);
            }else if(liZhi.getType()==2){
                jsHouse.dealWithRequest(liZhi);
            }else if(liZhi.getType()==3){
                gdHouse.dealWithRequest(liZhi);
            }else{
            System.out.println("沒有建立這樣的據(jù)點(diǎn),只能坐等死了榨崩。");
            }
        }
    }
}

⑧運(yùn)行結(jié)果

訂單需求:需要糯米糍30斤
貨物已由bj據(jù)點(diǎn)火速送出

訂單需求:需要桂味30斤
貨物已由js據(jù)點(diǎn)火速送出

訂單需求:需要槐味30斤
貨物已由gd據(jù)點(diǎn)火速送出

這個(gè)就是早期路人甲根據(jù)神秘高人Y方法建立的一套物流系統(tǒng)谴垫,路人甲覺得這套系統(tǒng)已經(jīng)完美無缺了,但是給神秘高人Y一看母蛛,立馬指出了一下問題:

  • Client的代碼臃腫翩剪,不同的荔枝品種就需要增加一個(gè)判斷,隨著品種的增多會(huì)造成if...else的判斷越來越多彩郊,很容易出現(xiàn)混亂前弯,可讀性不強(qiáng)。

  • 耦合過重秫逝,違背開閉原則恕出。

  • 異常處理不合理。如果路人甲把糯米糍的訂單直接發(fā)到了配送桂味的據(jù)點(diǎn)筷登,那么桂味據(jù)點(diǎn)無法處理這個(gè)訂單呀剃根,不處理的話那么路人甲就要坐等死了。

路人甲一看分析出這么多問題前方,立馬根據(jù)需求設(shè)計(jì)了一個(gè)新的物流系統(tǒng):訂單下來狈醉,由近到遠(yuǎn)的據(jù)點(diǎn)一個(gè)個(gè)分配訂單廉油,比如說桂味訂單到了bj據(jù)點(diǎn),bj據(jù)點(diǎn)處理不了苗傅,只能把這個(gè)訂單繼續(xù)往下一個(gè)據(jù)點(diǎn)傳遞抒线,到達(dá)js據(jù)點(diǎn)能夠處理就進(jìn)行配送,不需要再外下一個(gè)據(jù)點(diǎn)進(jìn)行傳遞了渣慕,即必然有一個(gè)唯一的據(jù)點(diǎn)給出唯一的答復(fù)嘶炭。

2.完善期

(1)據(jù)點(diǎn)UML
(2)改進(jìn)后的物流系統(tǒng)代碼

①荔枝的接口

public interface ILiZhi {
    //得到荔枝的品種
    public int getType();
    //每個(gè)倉(cāng)庫(kù)中得到荔枝訂單的請(qǐng)求
    public String getRequest();
}

②定義荔枝的具體類

public class LiZhi implements ILiZhi {

    //荔枝的種類,1代表糯米糍 2代表桂味 3代表槐味
    private int type=0;
    //荔枝的訂單需求
    private String request;

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

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

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

③定義一個(gè)抽象據(jù)點(diǎn)類

public abstract class ILiZhiHouse {

    //定義三種類型
    public final static int BJ_TYPE_REQUEST = 1;
    public final static int JS_TYPE_REQUEST = 2;
    public final static int GD_TYPE_REQUEST = 3;    

    //責(zé)任傳遞逊桦,下一個(gè)人責(zé)任人是誰
    private ILiZhiHouse liZhiHouse;
    //能處理的訂單
    private int type =0;

    public ILiZhiHouse(int type) {
        this.type = type;
    }

    //分發(fā)請(qǐng)求
    public void handleMessage(ILiZhi iLiZhi){
        if(iLiZhi.getType()==type){
            this.dealWithRequest(iLiZhi);
        }else{
            if(this.liZhiHouse!=null){
                this.liZhiHouse.handleMessage(iLiZhi);
            }else{
                System.out.println("沒有建立這樣的據(jù)點(diǎn)眨猎,只能坐等死了。");
            }
        }

    }
    //設(shè)置下一個(gè)據(jù)點(diǎn)是哪個(gè)
    public void setNext(ILiZhiHouse liZhiHouse){
        this.liZhiHouse=liZhiHouse;
    }
    //處理訂單需求
    public abstract void dealWithRequest(ILiZhi iLiZhi);
}

④bj據(jù)點(diǎn)强经,只有糯米糍荔枝

public class BJHouse extends ILiZhiHouse {

    public BJHouse() {
        super(BJ_TYPE_REQUEST);
    }

    @Override
    public void dealWithRequest(ILiZhi iLiZhi) {
        System.out.println("訂單需求:"+iLiZhi.getRequest());
        System.out.println("貨物已由bj據(jù)點(diǎn)火速送出");
    }
}

⑤js據(jù)點(diǎn)睡陪,只有桂味荔枝

public class JSHouse extends ILiZhiHouse {

    public JSHouse() {
        super(JS_TYPE_REQUEST);
    }

    @Override
    public void dealWithRequest(ILiZhi iLiZhi) {
        System.out.println("訂單需求:"+iLiZhi.getRequest());
        System.out.println("獲取已由js據(jù)點(diǎn)火速送出");
    }
}

⑥gd據(jù)點(diǎn),只有槐味荔枝

public class GDHouse extends ILiZhiHouse {
    public GDHouse() {
        super(GD_TYPE_REQUEST);
    }

    @Override
    public void dealWithRequest(ILiZhi iLiZhi) {
        System.out.println("訂單需求:"+iLiZhi.getRequest());
        System.out.println("獲取已由gd據(jù)點(diǎn)火速送出");
    }
}

⑦配送荔枝

public class Client {

    public static void main(String[] args) {
        //1代表糯米糍 2代表桂味 3代表槐味

        //路人甲根據(jù)楊貴妃的要求組裝需求
        List<ILiZhi> liZhis=new ArrayList<>();
        liZhis.add(new LiZhi(ILiZhiHouse.BJ_TYPE_REQUEST,"需要糯米糍30斤"));
        liZhis.add(new LiZhi(ILiZhiHouse.JS_TYPE_REQUEST,"需要桂味30斤"));
        liZhis.add(new LiZhi(ILiZhiHouse.GD_TYPE_REQUEST,"需要槐味30斤"));

        //定義三個(gè)據(jù)點(diǎn)
        BJHouse bjHouse=new BJHouse();
        JSHouse jsHouse=new JSHouse();
        GDHouse gdHouse=new GDHouse();
        //根據(jù)據(jù)點(diǎn)遠(yuǎn)近設(shè)定順序
        bjHouse.setNext(jsHouse);
        jsHouse.setNext(gdHouse);

        for(ILiZhi liZhi:liZhis){
            bjHouse.handleMessage(liZhi);
        }
    }
}

⑧運(yùn)行結(jié)果

訂單需求:需要糯米糍30斤
貨物已由bj據(jù)點(diǎn)火速送出

訂單需求:需要桂味30斤
貨物已由js據(jù)點(diǎn)火速送出

訂單需求:需要槐味30斤
貨物已由gd據(jù)點(diǎn)火速送出

業(yè)務(wù)調(diào)用類Client也不用去做判斷到底是需要誰去處理匿情,而且ILiZhiHouse抽象類的子類可以繼續(xù)增加下去兰迫,只需要擴(kuò)展傳遞鏈而已,調(diào)用類可以不用了解變化過程炬称,甚至是誰在處理這個(gè)請(qǐng)求都不用知道汁果。

二、基本概念

1.定義

使多個(gè)對(duì)象都有機(jī)會(huì)處理請(qǐng)求玲躯,從而避免了請(qǐng)求的發(fā)送者和接受者之間的耦合關(guān)系据德。將這些對(duì)象連成一條鏈,并沿著這條鏈傳遞該請(qǐng)求府蔗,直到有對(duì)象處理它為止晋控。

2.責(zé)任鏈的重點(diǎn)

責(zé)任鏈模式的重點(diǎn)是在“鏈”上,由一條鏈去處理相似的請(qǐng)求在鏈中決定誰來處理這個(gè)請(qǐng)求姓赤,并返回相應(yīng)的結(jié)果。

3.角色介紹

  • Handler
    抽象的處理者實(shí)現(xiàn)三個(gè)職責(zé):一是定義一個(gè)請(qǐng)求的處理方法handleMessage仲吏;二是定義一個(gè)鏈的編排方法setNext不铆,設(shè)置下一個(gè)處理者;三是定義了具體的請(qǐng)求者必須實(shí)現(xiàn)的兩個(gè)方法:定義自己能夠處理的級(jí)別和具體的處理任務(wù)裹唆。
  • ConcreteHandler
    責(zé)任鏈模式的核心在“鏈”上誓斥,“鏈”是由多個(gè)處理者ConcreteHandler組成的。具體的請(qǐng)求者必須實(shí)現(xiàn)的兩個(gè)方法:定義自己能夠處理的級(jí)別和具體的處理任務(wù)许帐。

三劳坑、責(zé)任鏈模式優(yōu)缺點(diǎn)

1.優(yōu)點(diǎn)

  • 責(zé)任鏈模式非常顯著的優(yōu)點(diǎn)是將請(qǐng)求和處理分開。請(qǐng)求者可以不用知道是誰處理的成畦,處理者可以不用知道請(qǐng)求的全貌距芬,涝开,兩者解耦,提高系統(tǒng)的靈活性

2.缺點(diǎn)

  • 性能問題框仔。每個(gè)請(qǐng)求都是從鏈頭遍歷到鏈尾舀武,特別是在鏈比較長(zhǎng)的時(shí)候,性能是一個(gè)非常大的問題

  • 調(diào)試不很方便离斩,特別是鏈條比較長(zhǎng)银舱,環(huán)節(jié)比較多的時(shí)候,由于采用了類似遞歸的方式跛梗,調(diào)試的時(shí)候邏輯可能比較復(fù)雜寻馏。

四、總結(jié)

鏈中節(jié)點(diǎn)數(shù)量需要控制核偿,避免出現(xiàn)超長(zhǎng)鏈的情況诚欠,一般的做法是在Handler中設(shè)置一個(gè)最大節(jié)點(diǎn)數(shù)量,在setNext方法中判斷是否已經(jīng)是超過其閾值宪祥,超過則不允許該鏈建立聂薪,避免無意識(shí)地破壞系統(tǒng)性能。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蝗羊,一起剝皮案震驚了整個(gè)濱河市藏澳,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌耀找,老刑警劉巖翔悠,帶你破解...
    沈念sama閱讀 222,729評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡叫挟,警方通過查閱死者的電腦和手機(jī)浦楣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,226評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來撮抓,“玉大人,你說我怎么就攤上這事摇锋〉ふ” “怎么了?”我有些...
    開封第一講書人閱讀 169,461評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵荸恕,是天一觀的道長(zhǎng)乖酬。 經(jīng)常有香客問我,道長(zhǎng)融求,這世上最難降的妖魔是什么咬像? 我笑而不...
    開封第一講書人閱讀 60,135評(píng)論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上县昂,老公的妹妹穿的比我還像新娘肮柜。我一直安慰自己,他們只是感情好七芭,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,130評(píng)論 6 398
  • 文/花漫 我一把揭開白布素挽。 她就那樣靜靜地躺著,像睡著了一般狸驳。 火紅的嫁衣襯著肌膚如雪预明。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,736評(píng)論 1 312
  • 那天耙箍,我揣著相機(jī)與錄音撰糠,去河邊找鬼。 笑死辩昆,一個(gè)胖子當(dāng)著我的面吹牛阅酪,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播汁针,決...
    沈念sama閱讀 41,179評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼术辐,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了施无?” 一聲冷哼從身側(cè)響起辉词,我...
    開封第一講書人閱讀 40,124評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎猾骡,沒想到半個(gè)月后瑞躺,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,657評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡兴想,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,723評(píng)論 3 342
  • 正文 我和宋清朗相戀三年幢哨,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片嫂便。...
    茶點(diǎn)故事閱讀 40,872評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡捞镰,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出毙替,到底是詐尸還是另有隱情曼振,我是刑警寧澤,帶...
    沈念sama閱讀 36,533評(píng)論 5 351
  • 正文 年R本政府宣布蔚龙,位于F島的核電站,受9級(jí)特大地震影響映胁,放射性物質(zhì)發(fā)生泄漏木羹。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,213評(píng)論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望坑填。 院中可真熱鬧抛人,春花似錦、人聲如沸脐瑰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,700評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽苍在。三九已至绝页,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間寂恬,已是汗流浹背续誉。 一陣腳步聲響...
    開封第一講書人閱讀 33,819評(píng)論 1 274
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留初肉,地道東北人酷鸦。 一個(gè)月前我還...
    沈念sama閱讀 49,304評(píng)論 3 379
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像牙咏,于是被迫代替她去往敵國(guó)和親臼隔。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,876評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容