本文源碼見:https://github.com/get-set/get-designpatterns/tree/master/chainofresponsibility
責(zé)任鏈模式(Chain of Responsibility Pattern)中兜畸,通常每個接收者都包含對另一個接收者的引用。如果一個對象不能處理該請求硫豆,那么它會把相同的請求傳給下一個接收者骂澄,依此類推边器。這種類型的設(shè)計模式屬于行為型模式兆龙。
例子
話不多說宛官,先看下邊兩個圖:
相信你一眼就明白了媒咳,沒錯迹缀,這就是責(zé)任鏈模式的現(xiàn)實場景使碾。
- 第一個是公司內(nèi)的責(zé)任鏈圖,作為基層員工祝懂,許多事情要請示票摇。有些事情組長就可以做主,有些事情要部門經(jīng)理才能批準(zhǔn)嫂易,但是作為基層員工通常不會直接越級找部門經(jīng)理兄朋,而是通過上級層層上報。
- 第二個是空氣凈化器的凈化流程,先過濾大顆粒粉塵颅和,然后是小顆粒PM2.5傅事,然后是吸附甲醛和異味物質(zhì)...
這兩個例子有些明顯的區(qū)別:
- 第一個例子,每個環(huán)節(jié)并不一定要做具體處理峡扩,有的直接轉(zhuǎn)給下一個環(huán)節(jié)去處理蹭越;而第二個例子,每個環(huán)節(jié)都會做相應(yīng)處理教届。
- 第一個例子响鹃,到具體執(zhí)行環(huán)節(jié)處理完后,就不會再向下一個節(jié)點流轉(zhuǎn)案训;而第二個例子买置,空氣要做過每層過濾處理最終才能出來。
這兩種其實都是責(zé)任鏈模式强霎。也就是責(zé)任鏈模式不care上述這些(白眼忿项,那你說這么多,浪費哥的寶貴時間)城舞。責(zé)任鏈模型關(guān)注于單個環(huán)節(jié)轩触,而不是整體流程。
這是我直接從《Java與模式》中截的圖家夺,看到這里是不是有種“鏈表”的既視感脱柱,要想做成“鏈”,每個節(jié)點就要有下一個節(jié)點的引用拉馋,然后每個節(jié)點有相應(yīng)的處理方法榨为,好啦,齊活啦~那么流程鏈呢椅邓,由外部業(yè)務(wù)邏輯去實現(xiàn)就OK了柠逞。
下面這個具體的代碼例子,我也是拿來主義景馁,哈哈,不是今天偷懶逗鸣,而是感覺這個例子挺好合住,來自菜鳥教程的關(guān)于日志的例子。
我們做應(yīng)用程序離不開日志撒璧,日志有多個等級透葛,通常從高到低有ERROR、WARN卿樱、INFO僚害、DEBUG等。
- 日志記錄的時候設(shè)置的等級越低繁调,那么就會記錄越多的日志萨蚕。比如要求日志記錄到DEBUG級靶草,那么ERROR、WARN岳遥、INFO奕翔、DEBUG這些日志都會打印出來;如果要求日志記錄到WARN級浩蓉,那么只打印出ERROR和WARN級別的日志派继。
- 日志打印在哪呢,有標(biāo)準(zhǔn)輸出捻艳、標(biāo)準(zhǔn)錯誤驾窟、文件等不同的輸出流。這些輸出流可以設(shè)置不同的日志級別认轨,就像上一條那樣绅络。
背景介紹完,可以看代碼了~
抽象類 AbstractLogger帶有詳細(xì)的日志記錄級別好渠。然后創(chuàng)建三種類型的記錄器昨稼,都擴展了AbstractLogger。每個記錄器消息的級別是否屬于自己的級別拳锚,如果是則相應(yīng)地打印出來假栓,否則將不打印并把消息傳給下一個記錄器。
創(chuàng)建抽象的記錄器類AbstractLogger
霍掺,ConsoleLogger
匾荆、ErrorLogger
和FileLogger
是擴展了的具體記錄器類。它們就是不同的責(zé)任鏈節(jié)點杆烁,根據(jù)自己的日志記錄級別打印出日志牙丽。
拿來主義的代碼就不貼了哈,可以看一下這個鏈接兔魂。
總結(jié)
關(guān)于責(zé)任鏈模式想必你有了一個感性的認(rèn)識烤芦,我不喜歡在文章中羅列設(shè)計模式的各種角色、使用場景析校、優(yōu)點构罗、缺點,感性的理解最重要智玻,說到底對設(shè)計模式的理解是對面向?qū)ο笤O(shè)計原則的理解遂唧。一方面,設(shè)計模式不僅僅是這23種吊奢,另一方面盖彭,具體某種設(shè)計模式在使用時也會有不同變化,不同的設(shè)計模式也可以結(jié)合使用。
因此設(shè)計模式要抓住特征召边,就像美術(shù)功底不高的人素描畫人物我們覺得不像铺呵,但是畫簡筆畫的人抓住特征畫出來的動漫大頭人形象,我們一看就知道這是誰掌实。一樣的道理陪蜻。
責(zé)任鏈模式的特征就在于“鏈”。如何實現(xiàn)這個鏈呢贱鼻,就是通過節(jié)點“接力”宴卖,每個節(jié)點指定好下個節(jié)點,這樣串起來就好邻悬。像流水線一樣症昏,各個節(jié)點處理自己分內(nèi)的工作。
好處也是顯而易見的父丰,就像流水線肝谭,如果增加了一道工具,那么接在合適的流程位置即可蛾扇,對于責(zé)任鏈模式來說沒有任何影響攘烛,因為流水線的構(gòu)造是由業(yè)務(wù)邏輯定義的。
責(zé)任鏈模式在許多我們熟知的Java框架或技術(shù)中都有應(yīng)用镀首。
- 寫過Servlet的同學(xué)肯定對
Filter
都有印象坟漱,我們可以定義多個Filter,這些Filter串起來就是一個“FilterChain”更哄,來自瀏覽器的請求過來之后芋齿,首先經(jīng)過層層Filter處理,然后到達(dá)映射的Servlet成翩,感覺是不是有點像“空氣過濾器”懊倮Α? - Tomcat中也應(yīng)用到了責(zé)任鏈模式麻敌,這個可能有些同學(xué)不太清楚栅炒。Tomcat是一個Servlet容器的實現(xiàn),我們編寫的Servlet就是用這個容器托管起來了术羔,請求進來之后职辅,由Tomcat接管,然后轉(zhuǎn)交給具體Servlet處理聂示,然后再有Tomcat將處理反饋發(fā)送回去。
- 其實這個容器是由一層層的類似于“俄羅斯套娃”的容器嵌套而成的簇秒,配置過Tomcat的server.xml的同學(xué)可能有印象鱼喉,標(biāo)簽層級是
<Server><Service><Engine><Host><Context></Context></Host></Engine></Service></Server>
我們的應(yīng)用程序作為<Context>
是配置在最里邊的。 - 那么請求進來之后,如果經(jīng)過層層套娃到達(dá)最里邊的<Context>也就是我們的應(yīng)用呢扛禽?就是用的責(zé)任鏈模式锋边,從Engine到Host再到Context一直到Wrapper(是的,還有一層套娃编曼。豆巨。。)都通過一個鏈傳遞請求掐场。