JavaScript設(shè)計模式之策略模式詳解
什么是設(shè)計模式?為什么需要學(xué)習(xí)設(shè)計模式?
學(xué)習(xí)設(shè)計模式的目的是:為了代碼可重用性、讓代碼更容易被他人理解、保證代碼可靠性。 設(shè)計模式使代碼編寫真正工程化;設(shè)計模式是軟件工程的基石脈絡(luò),如同大廈的結(jié)構(gòu)一樣。
經(jīng)典的設(shè)計模式有 23 種,但并不是每一種設(shè)計模式都被頻繁使用。在這里,介紹最常用和最實用的幾種設(shè)計模式,本文先來介紹策略模式(Strategy Pattern)。
策略模式是一種行為設(shè)計模式,定義一系列算法,將每一個算法封裝起來,并讓它們可以相互替換。策略模式讓算法獨立于使用它的客戶而變化,也稱為政策模式(Policy)。
假如正在開發(fā)一個在線商城的項目,每個產(chǎn)品都有原價,稱之為 originalPrice
。但實際上并非所有產(chǎn)品都以原價出售,可能會推出允許以折扣價出售商品的促銷活動。
商家可以在后臺為產(chǎn)品設(shè)置不同的狀態(tài),然后實際售價將根據(jù)產(chǎn)品狀態(tài)和原價動態(tài)調(diào)整。
具體規(guī)則如下:
- 部分產(chǎn)品已預(yù)售:為鼓勵客戶預(yù)訂,將在原價基礎(chǔ)上享受
20%
的折扣。 - 部分產(chǎn)品處于正常促銷階段:如果原價低于或等于
100
,則以10%
的折扣出售;如果原價高于100
,則減10
元。 - 有些產(chǎn)品沒有任何促銷活動:它們屬于
default
狀態(tài),并以原價出售。
這時需要寫一個獲取商品價格的函數(shù) getPrice
,應(yīng)該怎么寫呢?
function getPrice(originalPrice, status) { // ... // 返回價格; }
事實上,面對這樣的問題,如果不考慮任何設(shè)計模式,最直觀的寫法可能 if-else
多次條件判斷語句來計算價格。
有三種狀態(tài),可以快速編寫如下代碼:
function getPrice(originalPrice, status) { if (status === "pre-sale") { return originalPrice * 0.8; } if (status === "promotion") { if (origialPrice <= 100) { return origialPrice * 0.9; } else { return originalPrice - 20; } } if (status === "default") { return originalPrice; } }
有三個條件,上面的代碼寫了三個 if
語句,這是非常直觀的代碼,但是這段代碼組織上不好。
首先,它違反了單一職責(zé)原則(Single responsibility principle,規(guī)定每個類或者函數(shù)都應(yīng)該有一個單一的功能,并且該功能應(yīng)該由這個類或者函數(shù)完全封裝起來)。函數(shù) getPrice
做了太多的事情,這個函數(shù)不易閱讀,也容易出現(xiàn) bug
。如果一個條件出現(xiàn) bug
,整個函數(shù)就會崩潰。同時,這樣的代碼也不容易調(diào)試。
并且這段代碼很難應(yīng)對變化的需求,這時就需要考慮設(shè)計模式,其往往會在業(yè)務(wù)邏輯發(fā)生變化時展現(xiàn)出它的魅力。
假設(shè)業(yè)務(wù)擴大了,現(xiàn)在還有另一個折扣促銷:黑色星期五。折扣規(guī)則如下:
- 價格低于或等于 100 元的產(chǎn)品以 20% 的折扣出售。
- 價格高于 100 元但低于 200 元的產(chǎn)品將減少 20 元。
- 價格高于或等于 200 元的產(chǎn)品將減少 20 元。
這個時候該怎么擴展 getPrice
函數(shù)呢?
看起來必須在 getPrice
函數(shù)中添加一個條件語句:
function getPrice(originalPrice, status) { if (status === "pre-sale") { return originalPrice * 0.8; } if (status === "promotion") { if (origialPrice <= 100) { return origialPrice * 0.9; } else { return originalPrice - 20; } } // 黑色星期五規(guī)則 if (status === "black-friday") { if (origialPrice >= 100 && originalPrice < 200) { return origialPrice - 20; } else if (originalPrice >= 200) { return originalPrice - 50; } else { return originalPrice * 0.8; } } if (status === "default") { return originalPrice; } }
每當(dāng)增加或減少折扣時,都需要更改函數(shù)。這種做法違反了開閉原則(對擴展開放,對修改關(guān)閉)。修改已有的功能很容易出現(xiàn)新的錯誤,而且還會使得 getPrice
越來越臃腫。
那么如何優(yōu)化這段代碼呢?
首先,可以拆分這個函數(shù) getPrice
以減少臃腫。
/** * 預(yù)售商品價格規(guī)則 * @param {*} origialPrice * @returns */ function preSalePrice(origialPrice) { return originalPrice * 0.8; } /** * 促銷商品價格規(guī)則 * @param {*} origialPrice * @returns */ function promotionPrice(origialPrice) { if (origialPrice <= 100) { return origialPrice * 0.9; } else { return originalPrice - 20; } } /** * 黑色星期五促銷規(guī)則 * @param {*} origialPrice * @returns */ function blackFridayPrice(origialPrice) { if (origialPrice >= 100 && originalPrice < 200) { return origialPrice - 20; } else if (originalPrice >= 200) { return originalPrice - 50; } else { return originalPrice * 0.8; } } /** * 默認(rèn)商品價格 * @param {*} origialPrice * @returns */ function defaultPrice(origialPrice) { return origialPrice; } function getPrice(originalPrice, status) { if (status === "pre-sale") { return preSalePrice(originalPrice); } if (status === "promotion") { return promotionPrice(originalPrice); } if (status === "black-friday") { return blackFridayPrice(originalPrice); } if (status === "default") { return defaultPrice(originalPrice); } }
經(jīng)過這次修改,雖然代碼行數(shù)增加了,但是可讀性有了明顯的提升。getPrice
函數(shù)顯然沒有那么臃腫,寫單元測試也比較方便。
但是上面的改動并沒有解決根本的問題:代碼還是充滿了 if-else
,而且當(dāng)增加或者減少折扣規(guī)則的時候,仍然需要修改 getPrice
。
其實使用這些 if-else
的目的就是為了對應(yīng)狀態(tài)和折扣策略。
從圖中可以發(fā)現(xiàn),這個邏輯本質(zhì)上是一種映射關(guān)系:產(chǎn)品狀態(tài)與折扣策略的映射關(guān)系。
可以使用映射而不是冗長的 if-else
來存儲映射,按照這個思路可以構(gòu)造一個價格策略的映射關(guān)系(策略名稱與其處理函數(shù)之間的映射),如下:
const priceStrategies = { "pre-sale": preSalePrice, promotion: promotionPrice, "black-friday": blackFridayPrice, default: defaultPrice, };
將狀態(tài)與折扣策略結(jié)合起來,價格函數(shù)就可以優(yōu)化成如下:
function getPrice(originalPrice, status) { return priceStrategies[status](originalPrice); }
這時候如果需要加減折扣策略,不需要修改函數(shù),只需要修改價格策略映射關(guān)系 priceStrategies
之前的代碼邏輯如下:
優(yōu)化后的代碼邏輯如下:
以上的優(yōu)化策略就是使用了設(shè)計模式之策略模式,在實際的項目開發(fā)過程中還是比較實用。
在什么情況下可以考慮使用策略模式呢?如果函數(shù)具有以下特征:
- 判斷條件很多
- 各個判斷條件下的代碼相互獨立
然后可以將每個判斷條件下的代碼封裝成一個獨立的函數(shù),然后建立判斷條件和具體策略的映射關(guān)系。
總結(jié)
到此這篇關(guān)于JavaScript設(shè)計模式之策略模式的文章就介紹到這了,更多相關(guān)JavaScript策略模式內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- JS設(shè)計模式之策略模式概念與用法分析
- JavaScript設(shè)計模式之策略模式詳解
- javascript設(shè)計模式--策略模式之輸入驗證
- JavaScript設(shè)計模式之策略模式實例
- 學(xué)習(xí)JavaScript設(shè)計模式之策略模式
- 學(xué)習(xí)JavaScript設(shè)計模式(策略模式)
- javascript設(shè)計模式之策略模式學(xué)習(xí)筆記
- javascript設(shè)計模式 – 策略模式原理與用法實例分析
- JavaScript設(shè)計模式之策略模式實現(xiàn)原理詳解
- javascript設(shè)計模式之策略模式
相關(guān)文章
探索瀏覽器頁面關(guān)閉window.close()的使用詳解
這篇文章主要介紹了探索瀏覽器頁面關(guān)閉window.close()的使用詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08javascript中encodeURI和decodeURI方法使用介紹
encodeURI和decodeURI是成對來使用的,因為瀏覽器的地址欄有中文字符的話,可以會出現(xiàn)不可預(yù)期的錯誤,所以可以encodeURI把非英文字符轉(zhuǎn)化為英文編碼,decodeURI可以用來把字符還原回來2013-05-05js前端實現(xiàn)多圖圖片上傳預(yù)覽的兩個方法(推薦)
下面小編就為大家?guī)硪黄猨s前端實現(xiàn)多圖圖片上傳預(yù)覽的兩個方法(推薦)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-11-11javascript中typeof操作符和constucor屬性檢測
這篇文章主要介紹了javascript中typeof操作符和constucor屬性檢測的相關(guān)資料,需要的朋友可以參考下2015-02-02JavaScript使用lodash實現(xiàn)命名轉(zhuǎn)換和函數(shù)封裝
Lodash?是一個?JavaScript?的工具庫,它提供了一系列的函數(shù)來簡化代碼編寫,本文主要為大家介紹了如何使用lodash實現(xiàn)命名轉(zhuǎn)換和函數(shù)封裝,感興趣的小伙伴可以了解下2023-11-11重構(gòu)Javascript代碼示例(重構(gòu)前后對比)
回顧頭來看看那些Javascript腳本,有寫得不太理想,過于復(fù)雜?,F(xiàn)抽取出來,重構(gòu)它們,想學(xué)習(xí)javascript重構(gòu)的朋友可以參考下啊,網(wǎng)本文可以幫助你的2013-01-01