JavaScript 設計模式(二):策略模式

策略模式

策略模式:定義一系列的算法,把它們一個個封裝起來,並且使它們可以相互替換

生活小栗子:諸葛錦囊

諸葛給劉備的錦囊妙計,遇到任何困難都有應對計策。策略模式實現的也是類似的場景。

再來一慄:給喜歡的女生買冰淇淋,事先不瞭解其喜好,只能集齊各種味道,總會命種。就是比較 “費錢”,這也是策略模式的缺點,需事先考慮所有應對場景。

模式特點

  1. 策略類:算法封裝成獨立的函數/對象
  2. 環境類:根據不同參數調用對應的策略函數/對象執行

模式實現

實現方式:一個基於策略模式的程序至少由兩部分組成,第一個部分是一組策略類 Strategies(可變),策略類封裝類具體的算法,並負責具體的計算過程。第二個部分是環境類 Context(不變), Context 接收客戶的請求,隨後把請求委託給某一個策略類。

假設我們一個開發團隊,人員組成包括(開發組長,後端,前端,測試)。開發組長領取開發任務(不變),但具體的任務執行人員可根據類型劃分(可變)。

比如開發任務有以下幾項:

  1. 優化服務器緩存(後端任務)
  2. 優化首屏加載速度(前端任務)
  3. 完成系統併發測試(測試任務)

開發組長會根據任務類型,分發到對應的開發人員頭上,組長不承擔具體開發任務。所以每一個開發人員就承擔 Strategy 的作用(獨立的任務執行),而組長擁有並可支配所有開發人員的資源,充當 Context 的角色。團隊每一個開發人員“組合”起來就是一個 Strategies 類(執行開發任務)。 這個 Strategies 是可變的,如果說後續開發任務需要安卓的、IOS的支持,只要添加安卓、IOS開發人員配置即可(可擴展)。

// 策略類(開發人員)
var Strategies = {
    "backend": function(task) {
        console.log('進行後端任務:', task);
    },
    "frontend": function(task) {
        console.log('進行前端任務:', task);
    },
    "testend": function(task) {
        console.log('進行測試任務:', task);
    }
};

//  環境類(開發組長)
var Context = function(type, task) {
    typeof Strategies[type] === 'function' && Strategies[type](task);
}

Context('backend', '優化服務器緩存');
Context('frontend', '優化首頁加載速度');
Context('testend', '完成系統併發測試');

上述代碼帶來的好處:

  1. 算法獨立封裝,任務分發;
    開發組長不承擔具體開發任務(只做頂層設計,不跟年輕人搶飯碗)
  2. 複用性更好,不侷限於 Context 調用;
    開發人員不愁下家(去哪寫代碼都是寫代碼)

策略模式的另一個好處就是,消除了大部分的 if...else / switch...case 條件分支語句,代碼閱讀性提高。

// 沒有使用策略模式的組長...
var Context = function(type, task) {
    if (type === 'backend') {
        // 把後端給我叫來
    } else if (type === 'frontend') {
        // 把前端給我叫來
    } else if (type === 'testend') {
        // 把測試給我叫來
    }
}

JavaScript 中,函數作爲“一等公民“,也稱“一等對象”。JavaScript 中 ”高階函數“ 應用中,函數可被作爲變量或參數進行傳遞或調用。因此在 JavaScript 中,我們可將算法封裝成獨立的函數,並將它作爲參數傳遞給另一個函數調用。

// 封裝獨立的函數
var backend = function(task) {
    console.log('進行後端任務:', task);
};
var frontend = function(task) {
    console.log('進行前端任務:', task);
};
var testend = function(task) {
    console.log('進行測試任務:', task);
};

//  環境類(開發組長)
var Context = function(func, task) {
    typeof func === 'function' && func(task);
}

Context(backend, '優化服務器緩存');
Context(frontend, '優化首頁加載速度');
Context(testend, '完成系統併發測試');

少了 Strategies 策略類的外層包裹,函數更加獨立,並不妨礙其調用。使用函數替代策略類方式,正是我們日常開發中經常用到的 “隱形” 策略模式。

適用場景

  1. 多重條件語句判斷,執行對應的算法場景
  2. 表單校驗(validator)

優缺點

  • 優點:

    1. 利用組合、委託、多態的技術和思想,避免多重條件選擇語句 if...else/switch...case
    2. 複用性更高,算法函數可在系統其它地方使用;
    3. 支持設計模式 “開發-封閉原則“ ,算法封裝在獨立的 Strategy 中,易於維護和擴展;
    4. 策略模式使用 “組合和委託” 來讓 Context 擁有執行算法的能力,一種替換對象繼承的可行方案
  • 缺點:

    1. 增加了許多策略類或對象(開發人員職能劃分明確,人員成本有所增加);
    2. 必須瞭解各個 Strategy 的不同點,違反 “最少知識原則”(組長手底下有對應的開發人員,纔不用自己那麼苦逼)

JavaScript設計模式整理系列正在更新中,敬請關注專欄 "前端進擊的巨人",獲取實時更新。


參考文章

本文首發Github,期待Star!
https://github.com/ZengLingYong/blog

作者:以樂之名
本文原創,有不當的地方歡迎指出。轉載請指明出處。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章