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