Java設(shè)計(jì)模式之職責(zé)鏈模式

Java設(shè)計(jì)模式之職責(zé)鏈模式

前幾天復(fù)習(xí)java的異常處理時,接觸到了責(zé)任鏈模式以蕴。在企業(yè)級應(yīng)用中千劈,從前臺發(fā)過來的請求在后臺拋出異常祭刚,異常處理的設(shè)計(jì)一般會用到責(zé)任鏈模式,比如sql異常并不會直接拋出給前臺墙牌,而是經(jīng)過一系列的處理和再封裝袁梗,拋給前臺一個用戶可識別的異常信息。

1. 簡介

職責(zé)鏈模式有時候也叫責(zé)任鏈模式憔古,它是一種對象行為的設(shè)計(jì)模式。目的是:使多個對象都有機(jī)會處理請求淋袖,從而避免請求的發(fā)送者和接收者之間的耦合關(guān)系鸿市,將這些對象連成一條鏈,并沿著這條鏈傳遞該請求,直到有一個對象處理它為止焰情。發(fā)送請求的客戶端并不知道鏈上的哪個對象會處理這個請求陌凳,這使得系統(tǒng)在不影響客戶端的情況下動態(tài)的組織和分配責(zé)任。

這里需要注意的是内舟,職責(zé)鏈可以是一條直線合敦、一個環(huán)鏈或者是一個樹結(jié)構(gòu)的一部分。

Tomcat中的Filter就是使用了責(zé)任鏈模式验游。還有java中類加載使用的雙親委派模型也是典型的責(zé)任鏈模式充岛。

2. 參與者

職責(zé)鏈模式UML圖:

職責(zé)鏈模式UML.png

一個典型的職責(zé)鏈對象圖可能這樣的:

典型職責(zé)鏈對象圖.png
  • Handler:抽象處理者。定義一個處理請求的接口耕蝉,如果需要崔梗,接口可以定義 出一個方法以設(shè)定和返回對下家的引用。這個角色通常由一個Java抽象類或者Java接口實(shí)現(xiàn)垒在。上圖中Handler類的聚合關(guān)系給出了具體子類對下家的引用蒜魄,抽象方法handleRequest()規(guī)范了子類處理請求的操作。
  • ConcreteHandler:具體處理者场躯。具體處理者接受到請求后谈为,可以選擇立即處理這個請求或者將請求傳遞給下家。由于具體處理者持有對下家的引用踢关,因此伞鲫,如果需要,具體處理者可以訪問下家耘成。
  • Client:客戶端榔昔。向處理者提交請求對象。

3. 代碼示例

需求:部門搞活動向公司申請經(jīng)費(fèi)瘪菌,金額小于1000的項(xiàng)目經(jīng)理就可以審批撒会,小于2000的部門經(jīng)理可以審批,大于2000的只能總經(jīng)理審批师妙。

這里就使用職責(zé)鏈模式诵肛。申請人提交給項(xiàng)目經(jīng)理申請經(jīng)費(fèi),項(xiàng)目經(jīng)理如果權(quán)限夠的話就審批默穴,不夠的話就提交到上級領(lǐng)導(dǎo)怔檩,依次類推,直到能夠處理請求為止蓄诽。

代碼實(shí)現(xiàn):

Handler:審批人員的抽象父類

package com.wangjun.designPattern.chainOfResponsibility;

public abstract class Handler {
    protected Handler successor;
    
    public abstract boolean handler(int fee);
    
    public abstract void setSuccessor(Handler successor);
    
    public abstract Handler getSuccessot();
}

ConcreteHandler1:項(xiàng)目經(jīng)理

package com.wangjun.designPattern.chainOfResponsibility;

public class ProjectManager extends Handler {

    @Override
    public boolean handler(int fee) {
        if(fee <= 1000) {
            System.out.println("項(xiàng)目經(jīng)理:審批通過薛训。金額:" + fee);
            return true;
        }else {
            System.out.println("金額大于1000,項(xiàng)目經(jīng)理無權(quán)審批仑氛,移交給部門經(jīng)理乙埃!");
            setSuccessor(new DepartmentManager());
            return successor.handler(fee);
        }
    }

    @Override
    public void setSuccessor(Handler successor) {
        this.successor = successor;
    }

    @Override
    public Handler getSuccessot() {
        return successor;
    }

}

ConcreteHandler2:部門經(jīng)理

package com.wangjun.designPattern.chainOfResponsibility;

public class DepartmentManager extends Handler {

    @Override
    public boolean handler(int fee) {
        if(fee <= 2000) {
            System.out.println("部門經(jīng)理:審批通過闸英。金額:" + fee);
            return true;
        }else {
            System.out.println("金額大于2000,部門經(jīng)理無權(quán)審批介袜,移交給總經(jīng)理甫何!");
            setSuccessor(new Manager());
            return successor.handler(fee);
        }
    }

    @Override
    public void setSuccessor(Handler successor) {
        this.successor = successor;
    }

    @Override
    public Handler getSuccessot() {
        return successor;
    }

}

ConcreteHandler3:總經(jīng)理

package com.wangjun.designPattern.chainOfResponsibility;

public class Manager extends Handler {

    @Override
    public boolean handler(int fee) {
        if(fee <= 10000) {
            System.out.println("總經(jīng)理:審批通過。金額:" + fee);
            return true;
        }else {
            System.out.println("金額大于10000遇伞,審批不通過辙喂!申請金額:" + fee);
            return false;
        }
    }

    @Override
    public void setSuccessor(Handler successor) {
        this.successor = successor;
    }

    @Override
    public Handler getSuccessot() {
        return successor;
    }

}

Client:客戶端申請費(fèi)用

package com.wangjun.designPattern.chainOfResponsibility;

public class Client {

    public static void main(String[] args) {
        // 測試申請費(fèi)用列表
        int[] arr = {500, 1500, 2500, 29000}; 
        
        // 將申請?zhí)峤唤o的項(xiàng)目經(jīng)理
        ProjectManager pm = new ProjectManager();
        
        for(int i = 0; i < arr.length; i++) {
            System.out.println("第" + (i+1) + "筆費(fèi)用");
            if(pm.handler(arr[i])) {
                System.out.println("申請經(jīng)費(fèi)成功!");
            }else {
                System.out.print("申請經(jīng)費(fèi)失旔椤巍耗!");
            }
            System.out.println();
        }
    }

}

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

第1筆費(fèi)用
項(xiàng)目經(jīng)理:審批通過。金額:500
申請經(jīng)費(fèi)成功跳芳!

第2筆費(fèi)用
金額大于1000芍锦,項(xiàng)目經(jīng)理無權(quán)審批,移交給部門經(jīng)理飞盆!
部門經(jīng)理:審批通過娄琉。金額:1500
申請經(jīng)費(fèi)成功!

第3筆費(fèi)用
金額大于1000吓歇,項(xiàng)目經(jīng)理無權(quán)審批孽水,移交給部門經(jīng)理!
金額大于2000城看,部門經(jīng)理無權(quán)審批女气,移交給總經(jīng)理!
總經(jīng)理:審批通過测柠。金額:2500
申請經(jīng)費(fèi)成功炼鞠!

第4筆費(fèi)用
金額大于1000,項(xiàng)目經(jīng)理無權(quán)審批轰胁,移交給部門經(jīng)理谒主!
金額大于2000,部門經(jīng)理無權(quán)審批赃阀,移交給總經(jīng)理霎肯!
金額大于10000,審批不通過榛斯!申請金額:29000
申請經(jīng)費(fèi)失敼塾巍!

4. 總結(jié)

可以在一下情況下使用職責(zé)鏈模式:

  • 有多個對象可以處理同一個請求驮俗,哪個對象處理請求在運(yùn)行時刻自動確定懂缕;
  • 你想在不明確指定接受者的情況下,向多個對象的一個提交請求王凑;
  • 可處理一個請求的對象集合應(yīng)被動態(tài)指定提佣。

純的與不純的責(zé)任鏈模式

一個純的責(zé)任鏈模式要求一個具體的處理者對象只能在兩個行為中選擇一個:一是承擔(dān)責(zé)任吮蛹,二是把責(zé)任推給下家。不允許出現(xiàn)某一個具體處理者對象在承擔(dān)了一部分責(zé)任后又把責(zé)任向下傳的情況拌屏。

在一個純的責(zé)任鏈模式里面,一個請求必須被某一個處理者對象所接收术荤;在一個不純的責(zé)任鏈模式里面倚喂,一個請求可以最終不被任何接收端對象所接收。

純的責(zé)任鏈模式的實(shí)際例子很難找到瓣戚,一般看到的例子均是不純的責(zé)任鏈模式的實(shí)現(xiàn)端圈。有些人認(rèn)為不純的責(zé)任鏈根本不是責(zé)任鏈模式,這也許是有道理的子库。但是在實(shí)際的系統(tǒng)里舱权,純的責(zé)任鏈很難找到。如果堅(jiān)持責(zé)任鏈不純便不是責(zé)任鏈模式仑嗅,那么責(zé)任鏈模式便不會有太大意義了宴倍。

loading...

后續(xù)研究一下tomcat的Filter機(jī)制。

參考:

《設(shè)計(jì)模式》

https://blog.csdn.net/m13666368773/article/details/7702368

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末仓技,一起剝皮案震驚了整個濱河市鸵贬,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌脖捻,老刑警劉巖阔逼,帶你破解...
    沈念sama閱讀 221,635評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異地沮,居然都是意外死亡嗜浮,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評論 3 399
  • 文/潘曉璐 我一進(jìn)店門摩疑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來危融,“玉大人,你說我怎么就攤上這事未荒∽ㄅ玻” “怎么了?”我有些...
    開封第一講書人閱讀 168,083評論 0 360
  • 文/不壞的土叔 我叫張陵片排,是天一觀的道長寨腔。 經(jīng)常有香客問我,道長率寡,這世上最難降的妖魔是什么迫卢? 我笑而不...
    開封第一講書人閱讀 59,640評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮冶共,結(jié)果婚禮上乾蛤,老公的妹妹穿的比我還像新娘每界。我一直安慰自己,他們只是感情好家卖,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,640評論 6 397
  • 文/花漫 我一把揭開白布眨层。 她就那樣靜靜地躺著,像睡著了一般上荡。 火紅的嫁衣襯著肌膚如雪趴樱。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,262評論 1 308
  • 那天酪捡,我揣著相機(jī)與錄音叁征,去河邊找鬼。 笑死逛薇,一個胖子當(dāng)著我的面吹牛捺疼,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播永罚,決...
    沈念sama閱讀 40,833評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼啤呼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了尤蛮?” 一聲冷哼從身側(cè)響起媳友,我...
    開封第一講書人閱讀 39,736評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎产捞,沒想到半個月后醇锚,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,280評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡坯临,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,369評論 3 340
  • 正文 我和宋清朗相戀三年焊唬,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片看靠。...
    茶點(diǎn)故事閱讀 40,503評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡赶促,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出挟炬,到底是詐尸還是另有隱情鸥滨,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布谤祖,位于F島的核電站婿滓,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏粥喜。R本人自食惡果不足惜凸主,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,870評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望额湘。 院中可真熱鬧卿吐,春花似錦旁舰、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至衍腥,卻和暖如春绽快,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背紧阔。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留续担,地道東北人擅耽。 一個月前我還...
    沈念sama閱讀 48,909評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像物遇,于是被迫代替她去往敵國和親乖仇。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,512評論 2 359

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