JavaScript設計模式與開發實踐系列之策略模式

本系列爲《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

  1. calculateBonus函數比較龐大,包含了不少if語句,這些語句須要覆蓋全部的邏輯分支

  2. calculateBonus函數缺少彈性,若是增長新的績效等級,那咱們必須深刻calculateBonus內部,違反了開放-封閉原則

  3. 算法的複用性差,若是在程序的其餘地方須要重用這些計算獎金的算法,咱們的選擇只有複製和粘貼

所以,咱們要重構這段代碼。

使用組合函數重構代碼

咱們把各類算法封裝到一個個小函數裏面:

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版本的策略模式

上述代碼是模擬了一些傳統的面嚮對象語言的實現,實際上在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語言中,策略類每每被函數所替代,這是策略模式就成爲了一種隱形的模式。儘管這樣,完全瞭解策略模式,也有助於咱們明白使用函數的好處。

相關文章
相關標籤/搜索