我不喜歡寫 switch-case
語句,雖然相比於 Java 來講,Javascript 的 switch-case
要更爲強大,但仍是沒法避免該結構固有的缺陷。一樣的狀況發生自 if-else
結構,當分支變得複雜以後,編寫出來的代碼在閱讀上簡直就是災難。html
首先須要明確對於複雜的條件分支語句的短板到底在哪裏:git
對於程序員來講,上面的每一點都很致命。尤爲是當嵌套層次變深以後,在一個代碼塊的結尾處連續出現6、七個反大括號是一件很日常的事。程序員
這對於閱讀代碼的人來講簡直就是一個災難。github
尤爲是 switch-case
結構,不只有上面的問題,還有以下問題:設計模式
switch-case
結構的縮進有着不一樣的寫法,雖然不影響功能但也形成閱讀壓力;case
語句‘穿越’的狀況(忘記寫或者故意不寫 break
);case
語句不能獨享一個塊級做用域,在一個 case
語句中定義的變量不能在其餘 case
語句中重複定義;case
語句,若是有相同的操做,只能重複書寫。對於前面三點來講比較容易理解,最後一條能夠經過以下例子進行說明。bash
好比在我寫的2048這個遊戲中,有一段業務邏輯是這樣:app
switch (direction) {
case 'right':
if (canMoveRight()) {
moveRight();
}
break;
case 'left':
if (canMoveLeft()) {
moveLeft();
}
break;
case 'up':
if (canMoveUp()) {
moveUp();
}
break;
case 'down':
if (canMoveDown()) {
moveDown();
}
break;
}
複製代碼
明顯能夠看出上面這段代碼是有問題的,對於上下左右四個方向來講,執行的動做徹底是同樣的:判斷是否能夠移動,若是能夠則移動。既然如此,咱們就應該使用一種更清晰的結構。後面咱們可使用對象字面量進行靜態配置。post
複雜的條件分支語句除了帶來閱讀上的困難以外,還會帶來不易擴展的問題。這一點很好理解,好比須要增長條件分支時,就不得不在原有的代碼塊上進行修改,這是不符合程序的「開放-封閉」原則的。後面咱們可使用職責鏈模式進行優化。優化
仍是利用上面的2048遊戲的上下左右的移動邏輯,若是對 Javascript 有必定理解的程序員,不難寫出以下代碼:ui
const moveByDirectionMap = {
'right': [canMoveRight, moveRight],
'left': [canMoveLeft, moveLeft],
'up': [canMoveUp, moveUp],
'down': [canMoveDown, moveDown],
};
if (moveByDirectionMap[direction][0]()) {
moveByDirectionMap[direction][1]();
}
複製代碼
相比於 switch-case
結構,上面的代碼易讀性提升了很多,同時也避免了書寫重複的操做。
在個人2048中,能夠說上面這種組織代碼的方式獲得了充分體現。
對於分支之間沒有優先級區分的狀況來講,使用對象字面量進行靜態配置的方法就已經足夠駕馭了。然而在實際場景中,每每還會遇到各個條件分支之間是有優先判斷順序的,典型的就是 if-else
結構。
好比以下場景:
if (salary > 500) {
console.log('買耐克');
} else if (salary > 400) {
console.log('買阿迪達斯');
} else if (salary > 300) {
console.log('買李寧');
} else {
console.log('買安踏');
}
複製代碼
固然現實中若是真是這麼簡單的場景就行了,上面這段代碼用來表示存在優先級順序的條件分支語句。接下來使用職責鏈模式進行優化。
首先創建 ChainNode
類:
class ChainNode {
constructor(fn) {
this.fn = fn;
this.nextFn = null;
}
pass(...args) {
const res = Reflect.apply(this.fn, this, args);
return res === 'passNext' && this.nextFn
? Reflect.apply(this.nextFn.pass, this.nextFn, args)
: res;
}
}
複製代碼
而後將分支結構拆分:
const buyNike = salary => salary > 500 ? console.log('買耐克') : 'passNext';
const buyAdidas = salary => salary > 400 ? console.log('買阿迪達斯') : 'passNext';
const buyLining = salary => salary > 300 ? console.log('買李寧') : 'passNext';
const buyAnta = salary => console.log('買安踏');
複製代碼
最後,構建職責鏈:
// 封裝爲職責鏈結點
const buyNikeNode = new ChainNode(buyNike);
const buyAdidasNode = new ChainNode(buyAdidas);
const buyLiningNode = new ChainNode(buyLining);
const buyAntaNode = new ChainNode(buyAnta);
// 制定鏈條順序
buyNikeNode.nextFn = buyAdidasNode;
buyAdidasNode.nextFn = buyLiningNode;
buyLiningNode.nextFn = buyAntaNode;
// 只需調用第一個結點
buyNikeNode.pass(100);
複製代碼
能夠看到輸出:
買安踏
複製代碼
以上就是職責鏈模式的基本思想,能夠看出,當須要新增長條件分支時,只須要插入一個職責鏈結點便可,相比於原來的 if-else
結構,避免了修改關鍵邏輯代碼的危險和麻煩。須要注意的是,切不可濫用職責鏈模式,必須是當條件分支結構達到必定的複雜度而且須要優先級判斷時,才能使用,不然只會起到副作用。
寫出易讀易維護的代碼是每一個程序員的責任,這須要對程序語言自己和設計模式的的深刻理解。最後列出參考資料供感興趣的讀者繼續閱讀。