Javascript設計模式之策略模式

Gerryli發表於2019-06-20

  相信有不少人在開發過程中都遇到一大串的if/else判斷,程式碼又臭又長,而且隨著需求的增加與產品的迭代,判斷條件可能越來越長,越來越難以維護,有沒有什麼好的方式去解決這種弊端呢?答案是肯定的,策略模式是一種很好的解決辦法,可以幫助我們重構規範業務程式碼,提高程式碼的可讀性與可維護性。

  首先,策略模式什麼?

    策略模式就是定義一系列的演算法,把他們各自封裝成策略類,演算法被封裝在策略類內部的方法裡。在客戶對Context發起請求時,Context總是把請求委託給這些策略物件中間的某一個進行計算。

    簡單來說,就是將業務邏輯中公有的部分提取出來,封裝成一個環境類Context,具體的業務實現過程封裝在策略類strategies中,業務請求通過環境類去呼叫策略類中的實現過程,通過環境類給業務請求以響應訊息,其中環境類充當一層代理,將相似的業務邏輯進行總結歸納整理,提高程式碼的可讀性與可維護性。本文以表單驗證為例,來介紹策略模式在實際開發中的使用。

  策略模式最起碼由兩部分組成:

    1、策略類

      封裝某個策略的具體實現方法

    2、環境類Context

      接收客戶端業務處理請求,根據請求引數,呼叫策略類中具體的實現方法,策略類中必須要有處理方法;

  以表單驗證為例,其策略模式的實現過程如下:

function isFunction(fn) {
    return Object.prototype.toString.call(fn) === '[object Function]'
}

function isObject(obj) {
    return Object.prototype.toString.call(obj) === '[object Object]'
}
/**
 * 策略實現類
 */
const validateStrategies = {
    checkPattern: {
        mobileReg: /^(((13[0-9]{1})|(14[0-9]{1})|(15[0-9]{1})|(16[0-9]{1})|(17[0-9]{1})|(18[0-9]{1})|(19[0-9]{1}))+\d{8})$/,
        numReg: /^[0-9]+(\.[0-9]{1,2})?$/
    },
    validateMobile(item) {
        let flag = true;
        if (item.data && !this.checkPattern.mobileReg.test(item.data)) {
            flag = false;
        }
        return flag;
    },
    validateNumber(item) {
        let flag = true;
        if (!this.checkPattern.numReg.test(item.data)) {
            flag = false;
        } else if (item.point) {
            let pattern = new RegExp(`^[0-9]+(\.[0-9]{1,${item.point}})?$`);
            if (!pattern.test(item.data)) {
                flag = false;
            }
        }
        return flag;
    },
    //校驗欄位是否必填
    validateRequired(item) {
        let flag = true;
        if (!item.data) {
            flag = false;
        } else {
            flag = true;
        }
        return flag;
    }
};


class ValidateForm {

    constructor() {
        this.cache = [];
    }

    add(rules) {
        if (Array.isArray(rules)) {
            this.cache = [...this.cache, ...rules];
        } else if (isObject(rules)) {
            this.cache = [...this.cache, rules];
        } else {
            messagee.error(`引數型別應該為Object或Array,但是卻傳入了${typeof rules}`, 2);
        }

    }

    remove(id) {
        let index = this.cache.findIndex(vv => vv.id && Object.is(vv.id, id));
        this.cache.splice(index, 1);
    }

    start() {
        for (let i = 0, len = this.cache.length; i < len; i++) {
            let item = this.cache[i];
            //傳入的驗證方法必須是一個function
            if (!isFunction(validateStrategies[item.validateFun])) {
                message.error('表單校驗引數格式錯誤', 2);
                return false;
            } else {
                let flag = validateStrategies[item.validateFun](item);
                if (!flag) {
                    return flag;
                }
            }
        }
        return true;
    }
}

export default ValidateForm;

  上述程式碼,我們宣告瞭一個ValidateForm類和一個validateStrategies策略物件,策略物件中封裝了一些表單驗證方法,在例項化Validate表單驗證類的時候,需要往表單驗證類中注入特定的表單校驗規則,然後呼叫下啟動方法,就能得到表單驗證結果,以上程式碼使用過程如下:

 *  1、let validate = new ValidateForm();
 *  2、加入驗證規則 validate.add(rules:Array<Object>)
 *  3、開始校驗 validate.start()
 *  @param rules:Array<Object> {
 *    validateFun: '', //校驗方法
 *    data: '',//校驗的資料
 *    maxSize: '', //最大值
 *    ponit: '' //小數點
 *      ...
 *  }

  其中validateFun對應的是策略類中具體的方法名,其餘引數可以根據業務需求酌情新增。饒了一大圈,這麼做的好處是什麼呢?

    1、易維護擴充套件

      如表單驗證,在新增驗證規則時,只需要加入一個具體的策略實現方法即可,不需要額外改動其他業務程式碼

    2、可讀性較高

      熟悉某個具體功能邏輯不需要看一些不相關的內容

    3、程式碼複用性較高

      利用排列組合的方式,可以提高程式碼的複用性

  

相關文章