摘要:引言本文摘自設(shè)計模式與開發(fā)實(shí)踐在現(xiàn)實(shí)中,很多時候也有多種途徑到達(dá)同一個目的地。將不變的部分和變化的部分隔開是每個設(shè)計模式的主題,策略模式也不例外,策略模式的目的就是將算法的使用與算法的實(shí)現(xiàn)分離開來。一個基于策略模式的程序至少由兩部分組成。
引言
本文摘自《JavaScript設(shè)計模式與開發(fā)實(shí)踐》
在現(xiàn)實(shí)中,很多時候也有多種途徑到達(dá)同一個目的地。比如我們要去某個地方旅游,可以根據(jù)具體的實(shí)際情況來選擇出行的線路。
如果沒有時間但是不在乎錢,可以選擇坐飛機(jī)。
如果沒有錢,可以選擇坐大巴或者火車。
如果再窮一點(diǎn),可以選擇騎自行車。
在程序設(shè)計中,我們也常常遇到類似的情況,要實(shí)現(xiàn)某一個功能有多種方案可以選擇。比如一個壓縮文件的程序,既可以選擇zip算法,也可以選擇gzip算法。
這些算法靈活多樣,而且可以隨意互相替換。這種解決方案就是本文將要介紹的策略模式。
模式定義定義一系列的算法,把它們一個個封裝起來,并且使它們可以相互替換。
示例 計算年終獎很多公司的年終獎是根據(jù)員工的工資基數(shù)和年底績效情況來發(fā)放的。例如,績效為S的人年終獎有4倍工資,績效為A的人年終獎有3倍工資,而績效為B的人年終獎是2倍工資。假設(shè)財務(wù)部要求我們提供一段代碼,來方便他們計算員工的年終獎。
一般的實(shí)現(xiàn)var calculateBonus = function (performanceLevel, salary) { if (performanceLevel === "S") { return salary * 4; } if (performanceLevel === "A") { return salary * 3; } if (performanceLevel === "B") { return salary * 2; } }; // 測試 calculateBonus("B", 20000); // 輸出:40000 calculateBonus("S", 6000); // 輸出:24000
以上的實(shí)現(xiàn)存在下面的缺點(diǎn):
calculateBonus函數(shù)比較龐大,包含了很多if-else語句,這些語句需要覆蓋所有的邏輯分支。
calculateBonus函數(shù)缺乏彈性,如果增加了一種新的績效等級C,或者想把績效S的獎金系數(shù)改為5,那我們必須深入calculateBonus函數(shù)的內(nèi)部實(shí)現(xiàn),這是違反開放-封閉原則的。
算法的復(fù)用性差,如果在程序的其他地方需要重用這些計算獎金的算法呢?我們的選擇只有復(fù)制和粘貼
使用組合函數(shù)重構(gòu)代碼把計算年終獎的各種算法封裝到一個個的小函數(shù)里面,這些小函數(shù)有著良好的命名,可以一目了然地知道它對應(yīng)著哪種算法,它們也可以被復(fù)用在程序的其他地方。
var performanceS = function (salary) { return salary * 4; }; var performanceA = function (salary) { return salary * 3; }; var performanceB = function (salary) { return salary * 2; }; var calculateBonus = function (performanceLevel, salary) { if (performanceLevel === "S") { return performanceS(salary); } if (performanceLevel === "A") { return performanceA(salary); } if (performanceLevel === "B") { return performanceB(salary); } }; // 測試 calculateBonus("A", 10000); // 輸出:30000
重構(gòu)之后的代碼得到了一定的改善,但是依然沒有解決最重要的問題:calculateBonus函數(shù)有可能越來越龐大,而且在系統(tǒng)變化的時候缺乏彈性。
使用策略模式重構(gòu)代碼下面使用策略模式來重構(gòu)代碼。策略模式指的是定義一系列的算法,把它們一個個封裝起來。將不變的部分和變化的部分隔開是每個設(shè)計模式的主題,策略模式也不例外,策略模式的目的就是將算法的使用與算法的實(shí)現(xiàn)分離開來。
在這個例子里,算法的使用方式是不變的,都是根據(jù)某個算法取得計算后的獎金數(shù)額。而算法的實(shí)現(xiàn)是各異和變化的,每種績效對應(yīng)著不同的計算規(guī)則。
一個基于策略模式的程序至少由兩部分組成。第一個部分是一組策略類,策略類封裝了具體的算法,并負(fù)責(zé)具體的計算過程。 第二個部分是環(huán)境類Context,Context接受客戶的請求,隨后把請求委托給某一個策略類。要做到這點(diǎn),說明Context中要維持對某個策略對象的引用。
接近傳統(tǒng)面向?qū)ο笳Z言的實(shí)現(xiàn)// 定義每種計算年終獎的策略類 var performanceS = function () { }; performanceS.prototype.calculate = function (salary) { return salary * 4; }; var performanceA = function () { }; performanceA.prototype.calculate = function (salary) { return salary * 3; }; var performanceB = function () { }; performanceB.prototype.calculate = function (salary) { return salary * 2; }; // 定義獎金類Bonus(環(huán)境類Context) var Bonus = function () { this.salary = null; // 原始工資 this.strategy = null; // 績效等級對應(yīng)的策略對象 }; Bonus.prototype.setSalary = function (salary) { this.salary = salary; // 設(shè)置員工的原始工資 }; Bonus.prototype.setStrategy = function (strategy) { this.strategy = strategy; // 設(shè)置員工績效等級對應(yīng)的策略對象 }; Bonus.prototype.getBonus = function () { // 取得獎金數(shù)額 return this.strategy.calculate(this.salary); // 把計算獎金的操作委托給對應(yīng)的策略對象 }; // 測試 var bonus = new Bonus(); bonus.setSalary(10000); bonus.setStrategy(new performanceS()); // 設(shè)置策略對象 bonus.getBonus(); // 輸出:40000 bonus.setStrategy(new performanceA()); // 設(shè)置策略對象 bonus.getBonus(); // 輸出:30000使用JavaScript特性實(shí)現(xiàn)
// 直接定義為各個不同的方法 var strategies = { "S": function (salary) { return salary * 4; }, "A": function (salary) { return salary * 3; }, "B": function (salary) { return salary * 2; } }; // calculateBonus函數(shù)充當(dāng)環(huán)境類Context var calculateBonus = function (level, salary) { return strategies[level](salary); }; // 測試 calculateBonus("S", 20000); // 輸出:80000 calculateBonus("A", 10000); // 輸出:3000優(yōu)缺點(diǎn) 優(yōu)點(diǎn)
策略模式利用組合、委托和多態(tài)等技術(shù)和思想,可以有效地避免多重條件選擇語句。
策略模式提供了對開放—封閉原則的完美支持,將算法封裝在獨(dú)立的strategy中,使得它們易于切換,易于理解,易于擴(kuò)展。
策略模式中的算法也可以復(fù)用在系統(tǒng)的其他地方,從而避免許多重復(fù)的復(fù)制粘貼工作。
在策略模式中利用組合和委托來讓Context擁有執(zhí)行算法的能力,這也是繼承的一種更輕便的替代方案。
缺點(diǎn)策略模式也有一些缺點(diǎn),但這些缺點(diǎn)并不嚴(yán)重。
使用策略模式會在程序中增加許多策略類或者策略對象。
必須了解所有的策略類,必須了解各個策略類之間的不同點(diǎn),這樣才能選擇一個合適的策略類。此時策略類要向客戶暴露它的所有實(shí)現(xiàn),這是違反最少知識原則的。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/89808.html
摘要:策略模式可以避免代碼中的多重判斷條件。策略模式在程序中或多或少的增加了策略類。此文僅記錄本人閱讀設(shè)計模式與開發(fā)實(shí)踐這個本時的感受,感謝作者曾探寫出這么好的一本書。設(shè)計模式中很重要的一點(diǎn)就是將不變和變分離出來。參考設(shè)計模式與開發(fā)實(shí)踐曾探 策略模式的定義是:定義一系列的算法,把它們一個個封裝起來,并且是它們可以相互替換。 策略模式可以避免代碼中的多重判斷條件。 策略模式很好的體現(xiàn)了開放-...
摘要:本系列為設(shè)計模式與開發(fā)實(shí)踐作者曾探學(xué)習(xí)總結(jié),如想深入了解,請支持作者原版策略模式策略模式的定義定義一系列的算法,把它們一個個封裝起來,并且使它們可以互相替換。 本系列為《JavaScript設(shè)計模式與開發(fā)實(shí)踐》(作者:曾探)學(xué)習(xí)總結(jié),如想深入了解,請支持作者原版 策略模式 策略模式的定義:定義一系列的算法,把它們一個個封裝起來,并且使它們可以互相替換。 舉個形象的例子,使用策略模式計算...
摘要:訂閱模式的一個典型的應(yīng)用就是后面會寫一篇相關(guān)的讀書筆記。享元模式享元模式的核心思想是對象復(fù)用,減少對象數(shù)量,減少內(nèi)存開銷。適配器模式對目標(biāo)函數(shù)進(jìn)行數(shù)據(jù)參數(shù)轉(zhuǎn)化,使其符合目標(biāo)函數(shù)所需要的格式。 設(shè)計模式 單例模式 JS的單例模式有別于傳統(tǒng)面向?qū)ο笳Z言的單例模式,js作為一門無類的語言。使用全局變量的模式來實(shí)現(xiàn)單例模式思想。js里面的單例又分為普通單例和惰性單例,惰性單例指的是只有這個實(shí)例...
摘要:策略模式實(shí)現(xiàn)的也是類似的場景。第二個部分是環(huán)境類不變,接收客戶的請求,隨后把請求委托給某一個策略類。參考文章設(shè)計模式設(shè)計模式與開發(fā)實(shí)踐設(shè)計模式系統(tǒng)講解與應(yīng)用本文首發(fā),期待作者以樂之名本文原創(chuàng),有不當(dāng)?shù)牡胤綒g迎指出。 showImg(https://segmentfault.com/img/bVbugi7?w=800&h=600); 策略模式:定義一系列的算法,把它們一個個封裝起來,并且...
摘要:前端每周清單第期現(xiàn)狀分析與優(yōu)化策略單元測試爬蟲作者王下邀月熊編輯徐川前端每周清單專注前端領(lǐng)域內(nèi)容,以對外文資料的搜集為主,幫助開發(fā)者了解一周前端熱點(diǎn)分為新聞熱點(diǎn)開發(fā)教程工程實(shí)踐深度閱讀開源項目巔峰人生等欄目。 showImg(https://segmentfault.com/img/remote/1460000011008022); 前端每周清單第 29 期:Web 現(xiàn)狀分析與優(yōu)化策略...
閱讀 572·2023-04-25 16:00
閱讀 1614·2019-08-26 13:54
閱讀 2498·2019-08-26 13:47
閱讀 3420·2019-08-26 13:39
閱讀 1041·2019-08-26 13:37
閱讀 2740·2019-08-26 10:21
閱讀 3538·2019-08-23 18:19
閱讀 1606·2019-08-23 18:02