重構 - 設想API的擴大機制

1.媒介

上篇文章,重要引見了重構的一些觀點和一些簡樸的實例。這一次,細緻的說下項目中的一個重構場景–給API設想擴大機制。目標就是為了輕易今後能天真應對需求的轉變。固然了,是不是須要設想擴大性這個要看API的需求。假如人人有什麼發起,迎接批評留言。

2.擴大性表現形式

2-1.prototype

這個能夠說是JS內里最原的一個擴大。比方原生JS沒有供應打亂數組遞次的API,然則開闢者又想輕易運用,如許的話,就只能擴大數組的prototype。代碼以下

//擴大Array.prototype,增添打亂數組的要領。
Array.prototype.upset=function(){
    return this.sort((n1,n2)=>Math.random() - 0.5);
}

let arr=[1,2,3,4,5];
//挪用
arr.upset();
//顯現效果
console.log(arr);

運轉效果

《重構 - 設想API的擴大機制》

功用是完成了。然則上面的代碼,只想借用例子解說擴大性,人人看下就好。不要模擬,也不要在項目如許寫。如今基礎都制止如許開闢了。來由也很簡樸,之前的文章也有提到過。這裏反覆一下。

如許就污染了原生對象Array,他人建立的Array也會被污染,形成不必要的開支。最恐怖的是,萬一自身定名的跟原生的要領重名了,就被掩蓋本來的要領了。

Array.prototype.push=function(){console.log('等待')}  
let arrTest=[123]
arrTest.push()
//result:等待
//push要領有什麼作用,人人應當曉得,不曉得的能夠去w3c看下

《重構 - 設想API的擴大機制》

2-2.jQuery

關於 jQuery 的擴大性,離別供應了三個API:$.extend()、$.fn和$.fn.extend()。離別對jQuery的自身,靜態要領,原型對象舉行擴大,基於jQuery寫插件的時刻,最離不開的應當就是$.fn.extend()。

參考鏈接:

明白jquery的$.extend()、$.fn和$.fn.extend()
Jquery自定義插件之$.extend()、$.fn和$.fn.extend()

2-3.VUE

對VUE舉行擴大,援用官網(插件)的說法,擴大的體式格局平常有以下幾種:

1.增加全局要領或許屬性,如: vue-custom-element

2.增加全局資本:指令/過濾器/過渡等,如 vue-touch

3.經由過程全局 mixin 要領增加一些組件選項,如: vue-router

4.增加 Vue 實例要領,經由過程把它們增加到 Vue.prototype 上完成。

5.一個庫,供應自身的 API,同時供應上面提到的一個或多個功用,如 vue-router

基於VUE的擴大。在組件,插件的內容供應一個install要領。以下

《重構 - 設想API的擴大機制》

運用組件

《重構 - 設想API的擴大機制》

上面幾個擴大性的實例離別是原生對象,庫,框架的擴大,人人能夠以為有點誇誇而談,那下面就分享一個一樣平常開闢經常使用的一個實例。

3.實例-表單考證

看了上面那些擴大性的實例,下面看下一個在一樣平常開闢運用得也許多的一個實例:表單考證。這塊能夠說很簡樸,然則做好,做通用不簡樸。看了《JavaScript設想形式與開闢實踐》,用戰略形式對之前的表單考證函數舉行了一個重構。下面舉行一個簡樸的剖析。

下面的內容,代碼會偏多,雖然代碼不難,但照樣強烈發起人人不要只看,要邊看,邊寫,邊調試,不然作為讀者,極能夠不曉得我的代碼是什麼意義,很輕易懵。下面的代碼回涉兩個學問:開放-關閉準繩和戰略形式,人人能夠自行相識。

3-1.本來計劃

/**
 * @description 字段磨練
 * @param checkArr
 * @returns {boolean}
 */
function validateForm(checkArr){
    let _reg = null, ruleMsg, nullMsg, lenMsg;
    for (let i = 0, len = checkArr.length; i < len; i++) {
        //假如沒字段值是undefined,不再實行當前輪迴,實行下一次輪迴
        if (checkArr[i].el === undefined) {
            continue;
        }
        //設置劃定規矩毛病提醒信息
        ruleMsg = checkArr[i].msg || '字段花樣毛病';
        //設置值為空則毛病提醒信息
        nullMsg = checkArr[i].nullMsg || '字段不能為空';
        //設置長度毛病提醒信息
        lenMsg = checkArr[i].lenMsg || '字段長度局限' + checkArr[i].minLength + "至" + checkArr[i].maxLength;
        //假如該字段有空值校驗
        if (checkArr[i].noNull === true) {
            //假如字段為空,返回效果又提醒信息
            if (checkArr[i].el === "" || checkArr[i].el === null) {
                return nullMsg;
            }
        }
        //假若有該字段有劃定規矩校驗
        if (checkArr[i].rule) {
            //設置劃定規矩
            switch (checkArr[i].rule) {
                case 'mobile':
                    _reg = /^1[3|4|5|7|8][0-9]\d{8}$/;
                    break;
                case 'tel':
                    _reg = /^\d{3}-\d{8}|\d{4}-\d{7}|\d{11}$/;
                    break;
            }
            //假如字段不為空,而且劃定規矩毛病,返回毛病信息
            if (!_reg.test(checkArr[i].el) && checkArr[i].el !== "" && checkArr[i].el !== null) {
                return ruleMsg;
            }
        }
        //假如字段不為空而且長度毛病,返回毛病信息
        if (checkArr[i].el !== null && checkArr[i].el !== '' && (checkArr[i].minLength || checkArr[i].maxLength)) {
            if (checkArr[i].el.toString().length < checkArr[i].minLength || checkArr[i].el.toString().length > checkArr[i].maxLength) {
                return lenMsg;
            }
        }
    }
    return false;
}

函數挪用體式格局

    let testData={
        phone:'18819323632',
        pwd:'112'
    }

    let _tips = validateForm([
        {el: testData.phone, noNull: true, nullMsg: '電話號碼不能為空',rule: "mobile", msg: '電話號碼花樣毛病'},
        {el: testData.pwd, noNull: true, nullMsg: '暗碼不能為空',lenMsg:'暗碼長度不正確',minLength:6,maxLength:18}
    ]);
    //字段考證假如返回毛病信息
    if (_tips) {
        alert(_tips);
    }

3-2.存在題目

如許要領,置信人人看的也難熬痛苦,由於題目確實是比較多。

1.一個字段進入,能夠要經由三種推斷(空值,劃定規矩,長度)。假如只是一個簡樸的電話號碼劃定規矩校驗,就要經由其他兩種沒必要的校驗,形成不必要的開支。運轉的流程就犹如下面。

《重構 - 設想API的擴大機制》

2.劃定規矩校驗內里,只需這幾種校驗,假如要增添其他校驗,比方增添一個日期的劃定規矩,沒法完成。假如一向修正源碼,能夠會致使函數龐大。

3.寫法不文雅,挪用也不輕易。

3-3.替代計劃

針對上面2-2的三個題目,逐一舉行改良。

由於挪用體式格局就不輕易,很難在不轉變validateForm挪用體式格局的同時,優化重構內部的代碼,又增添擴大性。重寫這個要領又不能夠,由於有一般的處所已運用了這個API,自身一個一個的改不現實,所以就不修正這個validateForm,新建一個新的API:validate。在今後的項目上,也只管指導同事摒棄validateForm,運用新的API。

上面第一個,優化校驗劃定規矩,每次校驗(比方空值,長度,劃定規矩),都是一個簡樸的校驗,不再實行其他沒必要的校驗。運轉流程犹如下面。

《重構 - 設想API的擴大機制》

let validate = function (arr) {
    let ruleData = {
        /**
         * @description 不能為空
         * @param val
         * @param msg
         * @return {*}
         */
        isNoNull(val, msg){
            if (!val) {
                return msg
            }
        },
        /**
         * @description 最小長度
         * @param val
         * @param length
         * @param msg
         * @return {*}
         */
        minLength(val, length, msg){
            if (val.toString().length < length) {
                return msg
            }
        },
        /**
         * @description 最大長度
         * @param val
         * @param length
         * @param msg
         * @return {*}
         */
        maxLength(val, length, msg){
            if (val.toString().length > length) {
                return msg
            }
        },
        /**
         * @description 是不是是手機號碼花樣
         * @param val
         * @param msg
         * @return {*}
         */
        isMobile(val, msg){
            if (!/^1[3-9]\d{9}$/.test(val)) {
                return msg
            }
        }
    }
    let ruleMsg, checkRule, _rule;
    for (let i = 0, len = arr.length; i < len; i++) {
        //假如字段找不到
        if (arr[i].el === undefined) {
            return '字段找不到!'
        }
        //遍歷劃定規矩
        for (let j = 0; j < arr[i].rules.length; j++) {
            //提取劃定規矩
            checkRule = arr[i].rules[j].rule.split(":");
            _rule = checkRule.shift();
            checkRule.unshift(arr[i].el);
            checkRule.push(arr[i].rules[j].msg);
            //假如劃定規矩毛病
            ruleMsg = ruleData[_rule].apply(null, checkRule);
            if (ruleMsg) {
                //返回毛病信息
                return ruleMsg;
            }
        }
    }
};
let testData = {
    name: '',
    phone: '18819522663',
    pw: 'asda'
}
//校驗函數挪用
console.log(validate([
    {
        //校驗的數據
        el: testData.phone,
        //校驗的劃定規矩
        rules: [
            {rule: 'isNoNull', msg: '電話不能為空'}, {rule: 'isMobile', msg: '手機號碼花樣不正確'}
        ]
    },
    {
        el: testData.pw,
        rules: [
            {rule: 'isNoNull', msg: '電話不能為空'},
            {rule:'minLength:6',msg:'暗碼長度不能小於6'}
        ]
    }
]));

假如又有別的的劃定規矩,又得改這個,如許就違反了開放-關閉準繩。假如多人共用這個函數,劃定規矩能夠會許多,ruleData會變的龐大,形成不必要的開支。比方A頁面有金額的校驗,然則只需A頁面有。假如依據上面的體式格局改,在B頁面也會加載金額的校驗劃定規矩,然則基礎不會用上,形成資本糟蹋。

所以下面運用開放-關閉準繩。給函數的校驗劃定規矩增添擴大性。在實操之前,人人應當會懵,由於一個函數,能夠舉行校驗的操縱,又有增添校驗劃定規矩的操縱。一個函數做兩件事,就違反了單一準繩。到時刻也難庇護,所以引薦的做法就是分接口做。以下寫法。

let validate = (function () {
    let ruleData = {
        /**
         * @description 不能為空
         * @param val
         * @param msg
         * @return {*}
         */
        isNoNull(val, msg){
            if (!val) {
                return msg
            }
        },
        /**
         * @description 最小長度
         * @param val
         * @param length
         * @param msg
         * @return {*}
         */
        minLength(val, length, msg){
            if (val.toString().length < length) {
                return msg
            }
        },
        /**
         * @description 最大長度
         * @param val
         * @param length
         * @param msg
         * @return {*}
         */
        maxLength(val, length, msg){
            if (val.toString().length > length) {
                return msg
            }
        },
        /**
         * @description 是不是是手機號碼花樣
         * @param val
         * @param msg
         * @return {*}
         */
        isMobile(val, msg){
            if (!/^1[3-9]\d{9}$/.test(val)) {
                return msg
            }
        }
    }
    return {
        /**
         * @description 查詢接口
         * @param arr
         * @return {*}
         */
        check: function (arr) {
            let ruleMsg, checkRule, _rule;
            for (let i = 0, len = arr.length; i < len; i++) {
                //假如字段找不到
                if (arr[i].el === undefined) {
                    return '字段找不到!'
                }
                //遍歷劃定規矩
                for (let j = 0; j < arr[i].rules.length; j++) {
                    //提取劃定規矩
                    checkRule = arr[i].rules[j].rule.split(":");
                    _rule = checkRule.shift();
                    checkRule.unshift(arr[i].el);
                    checkRule.push(arr[i].rules[j].msg);
                    //假如劃定規矩毛病
                    ruleMsg = ruleData[_rule].apply(null, checkRule);
                    if (ruleMsg) {
                        //返回毛病信息
                        return ruleMsg;
                    }
                }
            }
        },
        /**
         * @description 增加劃定規矩接口
         * @param type
         * @param fn
         */
        addRule:function (type,fn) {
            ruleData[type]=fn;
        }
    }
})();
//校驗函數挪用-測試用例
console.log(validate.check([
    {
        //校驗的數據
        el: testData.mobile,
        //校驗的劃定規矩
        rules: [
            {rule: 'isNoNull', msg: '電話不能為空'}, {rule: 'isMobile', msg: '手機號碼花樣不正確'}
        ]
    },
    {
        el: testData.password,
        rules: [
            {rule: 'isNoNull', msg: '電話不能為空'},
            {rule:'minLength:6',msg:'暗碼長度不能小於6'}
        ]
    }
]));
//擴大-增加日期局限校驗
validate.addRule('isDateRank',function (val,msg) {
    if(new Date(val[0]).getTime()>=new Date(val[1]).getTime()){
        return msg;
    }
});
//測試新增加的劃定規矩-日期局限校驗
console.log(validate.check([
    {
        el:['2017-8-9 22:00:00','2017-8-8 24:00:00'],
        rules:[{
            rule:'isDateRank',msg:'日期局限不正確'
        }]
    }
    
]));

如上代碼所示,這裏須要往ruleData增加日期局限的校驗,這裡能夠增加。然則不能接見和修正ruleData的東西,有一個庇護的作用。另有一個就是,比方在A頁面增加日期的校驗,只在A頁面存在,不會影響別的頁面。假如日期的校驗在別的處所都能夠用上,就能夠斟酌,在全局內里為ruleData增加日期的校驗的劃定規矩。

至於第三個題目,如許的主意,能夠不算太文雅,挪用也不是太輕易,然則就我如今能想到的,這個就是最好計劃啊了。

這個看似是已做完了,然則人人能夠以為有一種狀況沒能應對,比方下面這類,做不到。

《重構 - 設想API的擴大機制》

由於上面的check接口,只需有一個毛病了,就立馬跳出了,不會校驗下一個。假如要完成下面的功用,就得完成,假若有一個值校驗毛病,就紀錄毛病信息,繼承校驗下一個,比及一切的校驗都實行完了以後,以下面的流程圖。

《重構 - 設想API的擴大機制》

代碼上面(人人先疏忽alias這個屬性)

let validate= (function () {
    let ruleData = {
        /**
         * @description 不能為空
         * @param val
         * @param msg
         * @return {*}
         */
        isNoNull(val, msg){
            if (!val) {
                return msg
            }
        },
        /**
         * @description 最小長度
         * @param val
         * @param length
         * @param msg
         * @return {*}
         */
        minLength(val, length, msg){
            if (val.toString().length < length) {
                return msg
            }
        },
        /**
         * @description 最大長度
         * @param val
         * @param length
         * @param msg
         * @return {*}
         */
        maxLength(val, length, msg){
            if (val.toString().length > length) {
                return msg
            }
        },
        /**
         * @description 是不是是手機號碼花樣
         * @param val
         * @param msg
         * @return {*}
         */
        isMobile(val, msg){
            if (!/^1[3-9]\d{9}$/.test(val)) {
                return msg
            }
        }
    }
    return {
        check: function (arr) {
            //代碼不反覆展現,上面一部分
        },
        addRule:function (type,fn) {
            //代碼不反覆展現,上面一部分
        },
        /**
         * @description 校驗一切接口
         * @param arr
         * @return {*}
         */
        checkAll: function (arr) {
            let ruleMsg, checkRule, _rule,msgArr=[];
            for (let i = 0, len = arr.length; i < len; i++) {
                //假如字段找不到
                if (arr[i].el === undefined) {
                    return '字段找不到!'
                }
                //假如字段為空以及劃定規矩不是校驗空的劃定規矩

                //遍歷劃定規矩
                for (let j = 0; j < arr[i].rules.length; j++) {
                    //提取劃定規矩
                    checkRule = arr[i].rules[j].rule.split(":");
                    _rule = checkRule.shift();
                    checkRule.unshift(arr[i].el);
                    checkRule.push(arr[i].rules[j].msg);
                    //假如劃定規矩毛病
                    ruleMsg = ruleData[_rule].apply(null, checkRule);
                    if (ruleMsg) {
                        //紀錄毛病信息
                        msgArr.push({
                            el:arr[i].el,
                            alias:arr[i].alias,
                            rules:_rule,
                            msg:ruleMsg
                        });
                    }
                }
            }
            //返回毛病信息
            return msgArr.length>0?msgArr:false;
        }
    }
})();
let testData = {
    name: '',
    phone: '188',
    pw: 'asda'
}
//擴大-增加日期局限校驗
validate.addRule('isDateRank',function (val,msg) {
    if(new Date(val[0]).getTime()>=new Date(val[1]).getTime()){
        return msg;
    }
});
//校驗函數挪用
console.log(validate.checkAll([
    {
        //校驗的數據
        el: testData.phone,
        alias:'mobile',
        //校驗的劃定規矩
        rules: [
            {rule: 'isNoNull', msg: '電話不能為空'}, {rule: 'isMobile', msg: '手機號碼花樣不正確'},{rule:'minLength:6',msg: '手機號碼不能少於6'}
        ]
    },
    {
        el: testData.pw,
        alias:'pwd',
        rules: [
            {rule: 'isNoNull', msg: '電話不能為空'},
            {rule:'minLength:6',msg:'暗碼長度不能小於6'}
        ]
    },
    {
        el:['2017-8-9 22:00:00','2017-8-8 24:00:00'],
        rules:[{
            rule:'isDateRank',msg:'日期局限不正確'
        }]
    }
]));

看到效果,如今一切的不合法的數據的紀錄都返回回來了。至於當時alias如今發表用途。
比方頁面是vue襯着的,依據alias能夠如許處置懲罰。

《重構 - 設想API的擴大機制》

《重構 - 設想API的擴大機制》

假如是jQuery襯着的,依據alias能夠如許處置懲罰。

《重構 - 設想API的擴大機制》

《重構 - 設想API的擴大機制》

3-4.向下兼容計劃

由於項目之前有運用了之前的校驗API,不能一道切,在之前的API沒燒毀之前,不能影響之前的運用。所以要重寫之前的validateForm,使之兼容如今的新API:validate。

    let validateForm=function (arr) {
        let _param=[],_single={};
        for(let i=0;i<arr.length;i++){
            _single={};
            _single.el=arr[i].el;
            _single.rules=[];
            //若有有非空磨練
            if(arr[i].noNull){
                _single.rules.push({
                    rule: 'isNoNull',
                    msg: arr[i].nullMsg||'字段不能為空'
                })
            }
            //假若有最小長度校驗
            if(arr[i].minLength){
                _single.rules.push({
                    rule: 'minLength:'+arr[i].minLength,
                    msg: arr[i].lenMsg ||'字段長度局限毛病'
                })
            }
            //假若有最大長度校驗
            if(arr[i].maxLength){
                _single.rules.push({
                    rule: 'maxLength:'+arr[i].maxLength,
                    msg: arr[i].lenMsg ||'字段長度局限毛病'
                })
            }
            //假若有劃定規矩校驗
            //校驗轉換劃定規矩
            let _ruleData={
                mobile:'isMobile'
            }
            if(arr[i].rule){
                _single.rules.push({
                    rule: _ruleData[arr[i].rule],
                    msg: arr[i].msg ||'字段花樣毛病'
                })
            }
            _param.push(_single);
        }
        let _result=validate.check(_param);
        return _result?_result:false;
    }
    let testData={
        phone:'18819323632',
        pwd:'112'
    }
    let _tips = validateForm([
        {el: testData.phone, noNull: true, nullMsg: '電話號碼不能為空',rule: "mobile", msg: '電話號碼花樣毛病'},
        {el: testData.pwd, noNull: true, nullMsg: '暗碼不能為空',lenMsg:'暗碼長度不正確',minLength:6,maxLength:18}
    ]);
    console.log(_tips)

4.小結

本日的例子就到這裏了,這個例子,不過就是給API增添擴大性。這個例子比較簡樸,不算難。人人用這個代碼在瀏覽器上運轉,就很好明白。假如人人對這個例子有什麼更好的發起,或許代碼上有什麼題目,迎接在批評區留言,人人多交換,互相進修。

————————-華美的分割線——————–

想相識更多,關注關注我的微信民眾號:等待書閣

《重構 - 設想API的擴大機制》

    原文作者:守候
    原文地址: https://segmentfault.com/a/1190000014751582
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞