JS設計模式初識(二)-策略模式

1、定義

策略模式的定義是:定義一系列的算法,把它們一個個封裝起來,而且使它們能夠相互替換。俗話說,條條大路通羅馬。在程序設計中,咱們也經常遇到相似的狀況,要實現某一個功能有多種方案能夠選擇。好比一個壓縮文件的程序,既能夠選擇zip算法,也能夠選擇 gzip 算法。算法

以公司發年終獎爲例:不少公司的年終獎是根據員工的工資基數和年末績效狀況來發放的。例如,績效爲 S 的人年 終獎有 4 倍工資,績效爲 A的人年終獎有3倍工資,而績效爲 B 的人年終獎是 2 倍工資。假設財務部要求咱們提供一段代碼,來方便他們計算員工的年終獎。設計模式

2.1 普通實現(bad)

// 普通實現
    function calcBonus({level, salary}) {
        if (level === 'S') {
            return salary * level * 4;
        }
        if (level === 'A') {
            return salary * level * 3;
        }
        if (level === 'B') {
            return salary * level * 2;
        }
    }
複製代碼

這個函數的缺點, 1. 若是狀況複雜的話可能會有多個if else; 2. 缺少彈性, 若是須要 增長一個新的level C 而且把S的level調成5 須要深刻代碼邏輯, 才能修改, 3. 代碼沒法複用bash

2.2 使用策略模式改進

const strategies = {
        'S': (salary) => salary * 4,
        'A': (salary) => salary * 3,
        'B': (salary) => salay * 2,
    }

    const calculateBonus = ({level, salary}) => strategies[level](salary);

    const t1 = calculateBonus({ level: 'S', salary: 20000 });
    console.log('t1 => ',t1);
複製代碼

經過使用策略模式重構代碼,咱們消除了原程序中大片的條件分支語句。全部跟計算獎金有 關的邏輯再也不放在 Context 中,而是分佈在各個策略對象中。Context 並無計算獎金的能力,而是把這個職責委託給了某個策略對象。每一個策略對象負責的算法已被各自封裝在對象內部。當咱們對這些策略對象發出「計算獎金」的請求時,它們會返回各自不一樣的計算結果,這正是對象多態性的體現,也是「它們能夠相互替換」的目的。替換 Context 中當前保存的策略對象,便能執行不一樣的算法來獲得咱們想要的結果。數據結構

2.3 策略模式的應用(以表單校驗爲例)

// 數據結構
[{
    strategy: 'minLength:6',
    errorMsg: '用戶名長度不能小於 10 位',
}]
複製代碼

基本代碼實現dom

import isString from 'lodash/isString';
    // 策略對象
    const strategies = {
        isNonEmpty: (value, errMsg) => {
            if (value === '') {
                return errMsg;
            }
        },
        minLength: (value, length, errMsg) => {
            if (val.length < length) {
                return errMsg;
            }
        },
        isMobil: (value, errMsg) => {
            var pattern = /^1[3|5|8][0-9]{9}$/ig;
            if (!pattern.text(value)) {
                return errMsg;
            }
        },
    };
    
    // 校驗器對象
    function Validator() {
        this.cache = []; // 保存校驗的函數
    }
    
    Validator.prototype.add = function(dom, rules) {
        const self = this;
        for(let i=0; i<rules.length; i++) {
            const rule = rules[i];
            const { strategy, errorMsg } = rule;
            const strategyArr = isString(strategy) && strategy.spilt(':');
            
            self.cache.push((() => {
                const strategy = strategyArr.shift();
                strategyArr.unshift(dom.value);
                strategyArr.push(errorMsg);
                return strategies[strategy].call(dom, ...strategyArr);
            }))

        }
    }

    Validator.prototype.valid = function() {
        let errorMsg;
        this.cache.every((item, index) => {
            const result = item();
            if (result) {
                errorMsg = result;
                return false;
            }
        });
        return errorMsg;
    }
複製代碼

客戶端調用方式函數

// 客戶端調用方式
    const userInput = document.getElementById('userInput');
    const form = document.getElementById('form');
    function validForm() {
        const v1 = new Validator();
        v1.add( userInput, [
            { 
                strategy: 'isNonEmpty',
                errorMsg: '用戶名不能爲空'
            }, {
                strategy: 'minLength:6',
                errorMsg: '用戶名長度不能小於 10 位'
            }]
        );
        const errorMsg = v1.valid();
        return errorMsg;
    }
    

    form.onsubmit = function() {
        const errorMsg = validForm();
        if (errorMsg) {
            alert(errorMsg);
            return false;
        }
    }
複製代碼

2.4 代碼改進

// 校驗器對象代碼改進
    class Validator {
        constructor() {
            this.cache = []; // 保存校驗的函數
        }
        add = (dom, rules) => {
            for(let i=0; i<rules.length; i++) {
                const rule = rules[i];
                const { strategy, errorMsg } = rule;
                const strategyArr = isString(strategy) && strategy.spilt(':');
                
                this.cache.push((() => {
                    const strategy = strategyArr.shift();
                    strategyArr.unshift(dom.value);
                    strategyArr.push(errorMsg);
                    return strategies[strategy].call(dom, ...strategyArr);
                }))

            }
        }
        valid = () => {
            let errorMsg;
            this.cache.every((item, index) => {
                const result = item();
                if (result) {
                    errorMsg = result;
                    return false;
                }
            });
            return errorMsg;
        }
    };
複製代碼

2、總結

策略模式是一種經常使用且有效的設計模式,也具備優勢和缺點ui

優勢
  • 策略模式利用組合、委託和多態等技術和思想,能夠有效地避免多重條件選擇語句。
  • 策略模式提供了對開放—封閉原則的完美支持,將算法封裝在獨立的 strategy 中,使得它們易於切換,易於理解,易於擴展。
  • 可複用
缺點
  • 要使用策略模式,必須瞭解全部的 strategy,必須瞭解各個 strategy 之間的不一樣點, 這樣才能選擇一個合適的 strategy。
  • 會增長策略類或者策略對象
相關文章
相關標籤/搜索