本系列爲《JavaScript設計模式與開發實踐》(做者:曾探)學習總結,如想深刻了解,請支持做者原版javascript
策略模式
的定義:定義一系列的算法,把它們一個個封裝起來,而且使它們能夠互相替換。java
舉個形象的例子,使用策略模式計算獎金
。算法
業務需求:設計模式
績效爲S
的人年終獎有4倍
工資函數
績效爲A
的人年終獎有3倍
工資學習
績效爲B
的人年終獎有2倍
工資this
財務部但願咱們能夠提供一段代碼,方便他們計算員工的年終獎。prototype
咱們能夠編寫一個名爲calculateBonus
的函數來計算員工的獎金數額,這個函數須要傳入兩個參數:工資數額
和績效等級
。代碼以下:設計
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
能夠發現,這段代碼很是簡單,可是存在着顯而易見的缺點。code
calculateBonus
函數比較龐大,包含了不少if語句,這些語句須要覆蓋全部的邏輯分支
calculateBonus
函數缺少彈性
,若是增長新的績效等級,那咱們必須深刻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接受客戶的請求,隨後把請求委託給某一個策略類。
如今咱們用策略模式來重構上邊的代碼。
//咱們先把每種績效的計算規則封裝在對應的策略類裏 var porformanceS = function() {}; porformanceS.prototype.calculate = function(salary) { return salary * 4; }; var porformanceA = function() {}; porformanceA.prototype.calculate = function(salary) { return salary * 3; }; var porformanceB = function() {}; porformanceB.prototype.calculate = function(salary) { return salary * 2; }; //接下來定義獎金類Bonus: 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); }
在完成最終的代碼以前,我們再來回顧一下策略模式的思想
:
定義一系列的算法,把它們一個個封裝起來,並且使它們能夠互相替換。
若是說的更詳細一點,就是:定義一系列的算法,把它們各自封裝成策略類,算法被封裝在策略類內部的方法裏。在客戶對Context發起請求的時候,Context老是把請求委託給這些策略對象中的某一個進行計算。
咱們繼續完成剛纔的代碼:
var Bonus = new Bonus(); bonus.setSalary(1000); bonus.setStrategy(new performanceS()); bonus.getBonus();
上述代碼是模擬了一些傳統的面嚮對象語言的實現,實際上在JavaScript
中,函數也是對象,因此更簡單和直接的作法是把strategy
直接定義爲函數:
var strategies = { "S": function ( salary ){ return salary * 4; }, "A": function ( salary ){ return salary * 3; }, "B": function ( salary ){ return salary * 2; } }; var calculateBonus=function(level,salary){ return strategies[level](salary); };
在JavaScript語言中,策略類
每每被函數所替代,這是策略模式就成爲了一種隱形
的模式。儘管這樣,完全瞭解策略模式,也有助於咱們明白使用函數的好處。