策略模式:定義一系列的算法甩卓,把它們一個個封裝起來凌节,并且使它們可以相互替換
生活小栗子:諸葛錦囊
諸葛給劉備的錦囊妙計,遇到任何困難都有應對計策亚隅。策略模式實現(xiàn)的也是類似的場景硼莽。
再來一栗:給喜歡的女生買冰淇淋,事先不了解其喜好枢步,只能集齊各種味道沉删,總會命中。就是比較 “費錢”醉途,這也是策略模式的缺點矾瑰,需事先考慮所有應對場景。
模式特點
- 策略類:算法封裝成獨立的函數(shù)/對象
- 環(huán)境類:根據(jù)不同參數(shù)調用對應的策略函數(shù)/對象執(zhí)行
模式實現(xiàn)
實現(xiàn)方式:一個基于策略模式的程序至少由兩部分組成隘擎,第一個部分是一組策略類 Strategies(可變)殴穴,策略類封裝類具體的算法,并負責具體的計算過程货葬。第二個部分是環(huán)境類 Context(不變)采幌, Context 接收客戶的請求,隨后把請求委托給某一個策略類震桶。
假設我們一個開發(fā)團隊休傍,人員組成包括(開發(fā)組長,后端蹲姐,前端磨取,測試)人柿。開發(fā)組長領取開發(fā)任務(不變),但具體的任務執(zhí)行人員可根據(jù)類型劃分(可變)忙厌。
比如開發(fā)任務有以下幾項:
- 優(yōu)化服務器緩存(后端任務)
- 優(yōu)化首屏加載速度(前端任務)
- 完成系統(tǒng)并發(fā)測試(測試任務)
開發(fā)組長會根據(jù)任務類型凫岖,分發(fā)到對應的開發(fā)人員頭上,組長不承擔具體開發(fā)任務逢净。所以每一個開發(fā)人員就承擔 Strategy 的作用(獨立的任務執(zhí)行)哥放,而組長擁有并可支配所有開發(fā)人員的資源,充當 Context 的角色爹土。團隊每一個開發(fā)人員“組合”起來就是一個 Strategies 類(執(zhí)行開發(fā)任務)甥雕。 這個 Strategies 是可變的,如果說后續(xù)開發(fā)任務需要安卓的胀茵、IOS的支持犀农,只要添加安卓、IOS開發(fā)人員配置即可(可擴展)宰掉。
// 策略類(開發(fā)人員)
var Strategies = {
"backend": function(task) {
console.log('進行后端任務:', task);
},
"frontend": function(task) {
console.log('進行前端任務:', task);
},
"testend": function(task) {
console.log('進行測試任務:', task);
}
};
// 環(huán)境類(開發(fā)組長)
var Context = function(type, task) {
typeof Strategies[type] === 'function' && Strategies[type](task);
}
Context('backend', '優(yōu)化服務器緩存');
Context('frontend', '優(yōu)化首頁加載速度');
Context('testend', '完成系統(tǒng)并發(fā)測試');
上述代碼帶來的好處:
- 算法獨立封裝,任務分發(fā)赁濒;
開發(fā)組長不承擔具體開發(fā)任務(只做頂層設計轨奄,不跟年輕人搶飯碗) - 復用性更好,不局限于 Context 調用拒炎;
開發(fā)人員不愁下家(去哪寫代碼都是寫代碼)
策略模式的另一個好處就是挪拟,消除了大部分的 if...else
/ switch...case
條件分支語句,代碼閱讀性提高击你。
// 沒有使用策略模式的組長...
var Context = function(type, task) {
if (type === 'backend') {
// 把后端給我叫來
} else if (type === 'frontend') {
// 把前端給我叫來
} else if (type === 'testend') {
// 把測試給我叫來
}
}
JavaScript 中玉组,函數(shù)作為“一等公民“,也稱“一等對象”丁侄。JavaScript 中 ”高階函數(shù)“ 應用中惯雳,函數(shù)可被作為變量或參數(shù)進行傳遞或調用。因此在 JavaScript 中鸿摇,我們可將算法封裝成獨立的函數(shù)石景,并將它作為參數(shù)傳遞給另一個函數(shù)調用。
// 封裝獨立的函數(shù)
var backend = function(task) {
console.log('進行后端任務:', task);
};
var frontend = function(task) {
console.log('進行前端任務:', task);
};
var testend = function(task) {
console.log('進行測試任務:', task);
};
// 環(huán)境類(開發(fā)組長)
var Context = function(func, task) {
typeof func === 'function' && func(task);
}
Context(backend, '優(yōu)化服務器緩存');
Context(frontend, '優(yōu)化首頁加載速度');
Context(testend, '完成系統(tǒng)并發(fā)測試');
少了 Strategies 策略類的外層包裹拙吉,函數(shù)更加獨立潮孽,并不妨礙其調用。使用函數(shù)替代策略類方式筷黔,正是我們?nèi)粘i_發(fā)中經(jīng)常用到的 “隱形” 策略模式往史。
適用場景
- 多重條件語句判斷,執(zhí)行對應的算法場景
- 表單校驗(validator)
優(yōu)缺點
- 優(yōu)點:
- 利用組合佛舱、委托椎例、多態(tài)的技術和思想挨决,避免多重條件選擇語句
if...else/switch...case
; - 復用性更高粟矿,算法函數(shù)可在系統(tǒng)其它地方使用凰棉;
- 支持設計模式 “開發(fā)-封閉原則“ ,算法封裝在獨立的 Strategy 中陌粹,易于維護和擴展撒犀;
- 策略模式使用 “組合和委托” 來讓 Context 擁有執(zhí)行算法的能力,一種替換對象繼承的可行方案
- 利用組合佛舱、委托椎例、多態(tài)的技術和思想挨决,避免多重條件選擇語句
- 缺點:
- 增加了許多策略類或對象(開發(fā)人員職能劃分明確掏秩,人員成本有所增加)或舞;
- 必須了解各個 Strategy 的不同點,違反 “最少知識原則”(組長手底下有對應的開發(fā)人員蒙幻,才不用自己那么苦逼)
參考文章
本文首發(fā)Github映凳,期待Star!
https://github.com/ZengLingYong/blog
作者:以樂之名
本文原創(chuàng)邮破,有不當?shù)牡胤綒g迎指出诈豌。轉載請指明出處。