一說到「設計模式」,可能很多人都有聽過。 但是如果真的要你說說應用場景,可能會有點「難以描述」。 除了應用場景比較多的 單例模式 你能夠 信手拈來 ,其他的可能會覺得有點難以掌握。也許壓根都沒用過。 今天,通過本篇文章,讓你對 責任鏈模式 也能夠信手拈來。 本篇文章通過實際項目中的例子來讓你認識何為 ...
一說到「設計模式」,可能很多人都有聽過。
但是如果真的要你說說應用場景,可能會有點「難以描述」。
除了應用場景比較多的單例模式你能夠信手拈來,其他的可能會覺得有點難以掌握。也許壓根都沒用過。
今天,通過本篇文章,讓你對責任鏈模式也能夠信手拈來。
本篇文章通過實際項目中的例子來讓你認識何為責任鏈模式。
定義
百度百科的介紹:責任鏈模式是一種設計模式。在責任鏈模式里,很多對象由每一個對象對其下家的引用而連接起來形成一條鏈。請求在這個鏈上傳遞,直到鏈上的某一個對象決定處理此請求。發出這個請求的客戶端並不知道鏈上的哪一個對象最終處理這個請求,這使得系統可以在不影響客戶端的情況下動態地重新組織和分配責任。
維基百科的介紹:責任鏈模式在面向對象程式設計里是一種軟體設計模式,它包含了一些命令對象和一系列的處理對象。每一個處理對象決定它能處理哪些命令對象,它也知道如何將它不能處理的命令對象傳遞給該鏈中的下一個處理對象。該模式還描述了往該處理鏈的末尾添加新的處理對象的方法。
我的介紹:顧名思義,責任鏈模式是一條鏈,鏈上有多個節點,每個節點都有各自的責任。當有輸入時,第一個責任節點看自己能否處理該輸入,如果可以就處理。如果不能就交由下一個責任節點處理。依次類推,直到最後一個責任節點。
定義總是有點文縐縐,還是看下下麵的例子加深下理解吧。
例子
舉幾個例子:
1. 需求開發例子
假設現在有個需求來了,首先是實習生拿到這個需求。
如果實習生能夠實現,直接實現。如果不行,他把這個需求交給初級工程師。
如果初級工程師能夠實現,直接實現。如果不行,交給中級工程師。
如果中級工程師能夠實現,直接實現。如果不行,交給高級工程師。
如果高級工程師能夠實現,直接實現。如果不行,交給 CTO。
如果 CTO能夠實現,直接實現。如果不行,直接跟產品說,需求不做。
對於程式員來說,沒有實現不了的需求,只有不想做的需求
2. 買球籃例子
假設你現在有個籃球,然後想要買個球籃。
你肯定是到店裡,讓老闆把所有尺寸的球籃拿出來。
然後你一個一個試。
第一個不行,就第二個。
第二個不行,就第三個。
...
直到找到合適的。
通過定義和列舉的例子,大家對於責任鏈模式應該有點熟悉了。
是不是覺得自己平時寫的代碼中好像有用到的樣子,有點熟悉?
不要急,接下來我們給大家看看一些熟悉的代碼,這裡以 Java 代碼為例子,其他語言也是類似的。
場景
給定一個輸入值,根據輸入值執行不同邏輯。
我們一看,分分鐘寫出如下代碼:
String input = "1";
if ("1".equals(input)) {
//TODO do something
} else if ("2".equals(input)) {
//TODO do something
} else if ("3".equals(input)) {
//TODO do something
}
或者如下代碼:
String input = "1";
switch (input) {
case "1":
//TODO do something
break;
case "2":
//TODO do something
break;
case "3":
//TODO do something
break;
default:
//TODO do something
break;
}
如果每個分支裡面的邏輯比較簡單,那還好,如果邏輯複雜,假設每個 case 大概要 100 行代碼處理,有 10 個 case,一下子就出來一個「千行代碼」文件。而且還不利於維護、測試和擴展。
如果能夠想辦法把代碼拆分成每個 case 一個文件,這樣不僅代碼邏輯清晰了很多,而且不管是後續維護、擴展還是進行測試,都方便很多。
因此,本篇文章核心,責任鏈模式的妙用——拆分代碼就來了。
責任鏈模式拆分代碼
這裡以上面場景為例子進行拆分代碼說明,其他場景相信大家能夠舉一反三。
1. 定義一個抽象類。
public abstract class BaseCase {
// 為 true 表明自己可以處理該 case
private boolean isConsume;
public BaseCase(boolean isConsume) {
this.isConsume = isConsume;
}
// 下一個責任節點
private BaseCase nextCase;
public void setNextCase(BaseCase nextCase) {
this.nextCase = nextCase;
}
public void handleRequest() {
if (isConsume) {
// 如果當前節點可以處理,直接處理
doSomething();
} else {
// 如果當前節點不能處理,並且有下個節點,交由下個節點處理
if (null != nextCase) {
nextCase.handleRequest();
}
}
}
abstract protected void doSomething();
}
註釋已經寫的很清楚了。這裡就不再贅述。
2. 各個 case 來實現該抽象類。
這裡列舉一個 case,其他可以看代碼。
public class OneCase extends BaseCase {
public OneCase(boolean isConsume) {
super(isConsume);
}
@Override protected void doSomething() {
// TODO do something
System.out.println(getClass().getName());
}
}
3. 初始化各個 case,並指定每個 case 的下一個節點。
String input = "1";
OneCase oneCase = new OneCase("1".equals(input));
TwoCase twoCase = new TwoCase("2".equals(input));
DefaultCase defaultCase = new DefaultCase(true);
oneCase.setNextCase(twoCase);
twoCase.setNextCase(defaultCase);
oneCase.handleRequest();
好了,到此我們責任鏈模式拆分代碼就告一段落了。
一個優化
上面是責任鏈模式拆分代碼的一個基本實現。
後面有同事給了建議,說可以參考 OkHttp 裡面的 Interceptor 實現。
所以這邊看了一下,做瞭如下改進。
先說一下大概思想吧。
將所有的 case 集中起來,通過遍歷確定能夠處理的 case。
同樣是以上面的場景為例進行說明。
1. 定義一個介面。
interface BaseCase {
// 所有 case 處理邏輯的方法
void doSomething(String input, BaseCase baseCase);
}
2. 建立一個責任鏈管理類,管理所有 case。
public class CaseChain implements BaseCase {
// 所有 case 列表
private List<BaseCase> mCaseList = new ArrayList<>();
// 索引,用於遍歷所有 case 列表
private int index = 0;
// 添加 case
public CaseChain addBaseCase(BaseCase baseCase) {
mCaseList.add(baseCase);
return this;
}
@Override public void doSomething(String input, BaseCase baseCase) {
// 所有遍歷完了,直接返回
if (index == mCaseList.size()) return;
// 獲取當前 case
BaseCase currentCase = mCaseList.get(index);
// 修改索引值,以便下次回調獲取下個節點,達到遍歷效果
index++;
// 調用 當前 case 處理方法
currentCase.doSomething(input, this);
}
}
3. 各個 case 實現介面。這裡以其中一個為例。
public class OneCase implements BaseCase {
@Override
public void doSomething(String input, BaseCase baseCase) {
if ("1".equals(input)) {
// TODO do something
System.out.println(getClass().getName());
return;
}
//當前沒法處理,回調回去,讓下一個去處理
baseCase.doSomething(input, baseCase);
}
}
4. 初始化各個 case
String input = "1";
CaseChain caseChain = new CaseChain();
caseChain.addBaseCase(new OneCase())
.addBaseCase(new TwoCase())
.addBaseCase(new DefaultCase());
caseChain.doSomething(input, caseChain);
好了,註釋寫的很清楚,相信大家看懂是沒問題的。
至此,我們的責任鏈模式已經講完了。
相信你對於責任鏈模式已經熟記於心了。
如果你還有點疑問
可以留言,看下代碼或者敲敲代碼。
總結
本篇文章以實際項目中的場景為例,向你描述責任鏈模式的妙用。
看完文章,可能你只學到其形,而沒有學到其神。
通過不斷的使用以及自己經驗的不斷積累,相信達到形神兼備也是時間問題而已。
等你完全掌握之後,不再是「我要用責任鏈模式,因此寫出了代碼」。
而是「我寫出了代碼,才發現用到了責任鏈模式」。
正如《倚天屠龍記》裡面張三豐教張無忌太極劍時,最後張無忌全都忘了一樣。
溫馨提示:
學習了新設計模式,難免有點手癢。
但是切記不要濫用設計模式。
不要為了設計而設計。
比如你就幾個 case,而且處理邏輯就是彈個框。
你說你要用上設計模式?這樣成本會更高,其實沒必要。
所以學會是一回事,什麼時候用又是另一回事了。
覺得不錯,歡迎轉發分享。
參考:
http://www.runoob.com/design-pattern/chain-of-responsibility-pattern.html
https://www.jianshu.com/p/8a157cb73434