一個價格校驗器的設計實現

如何才能更好的實現一個價格金額校驗器,需要滿足的需求如下:

1. 正數,最多兩位小數

2. 數值有區間限制

3. 錯誤要有錯誤提示

第一種方案:函數式實現

function myfunction(min, max, pointLength){ var numberDom = window.document.getElementsByTagName(input)[0]; var a = numberDom.value; var reg = /(^[-+]?[1-9]d{0,8}(.d{1,2})?$)|(^[-+]?[0]{1}(.d{1,2})?$)/; // 判定是否是數字 if(a !== ){ if(!reg.test(a)){ window.document.getElementsByTagName(p)[0].innerHTML = "請輸入合法的數字:" + min + "-" + max + "之間的數字,並且最多" + pointLength + "位小數"; } else { window.document.getElementsByTagName(p)[0].innerHTML = "校驗通過"; } } else { window.document.getElementsByTagName(p)[0].innerHTML = "請輸入數字"; }}

備註:這個方案的缺點:

1. 代碼龐大,if-else較多,如果再有限制條件,只能添加if else;

2. 函數缺乏彈性,如果再有校驗規則,則只能改函數,不符合開放-封閉原則

3. 演算法的復用性差,如果有別的地方使用,只能拷貝代碼

第二種方案:策略模式引入

基於上面的問題,我們能不能像寫配置一樣去寫這個驗證呢?實現的基本邏輯是這樣的:

// 獲取校驗元素var numberDom = window.document.getElementsByTagName(input)[0];// 根據校驗規則,生成校驗器var validator = new Validator();validator.add(numberDom.value, isNotEmpty, 請輸入數字);validator.add(numberDom.value, minLength: 1, 用戶輸入不能少於1);// 開始校驗,輸入校驗元素,輸出錯誤信息var errorObj = validator.start();// 根據錯誤信息進行提示if(errorObj.errorMsg){ console.log(errorObj.errorMsg);} else { console.log(校驗通過);}

什麼是策略模式?

它的核心思想是將做什麼和誰去做相分離,一個完整的策略模糊包括策略類,環境類,就像出去旅行,有多種交通工具,交通工具就相當於策略類,在本次實踐中,給出的三個條件就是三個驗證方法,多個驗證方法組成了策略類,代碼重構如下:

var strategies = { isNubmer(value, errorMessage){ return value.isNaN ? errorMessage : void 0; }, minLength(value, length, errorMessage){ return value.length < length ? errorMessage : void 0; }, maxLength(value, length, errorMessage){ return value.length > length ? errorMessage : void 0; }, pointLength(value, length, errorMessage){ return value.length > length ? errorMessage : void 0; }}

我們完成了具體策略類的編寫,接下來需要以可配置化的方式執行策略類,我們稱它為角色類,角色類的作用是接收請求但是不處理請求,請求交給策略類力的具體方法處理,代碼如下:

class Validator { constructor(){ this.cache = []; // 代碼內容 } add(dom, rules){ for (var rule of rules) { var strategyRule = rule.strategy.split(:); var errorMsg = rule.errorMsg; this.cache.push(() => { var strategy = strategyRule.shift(); strategyRule.unshift(dom.value); strategyRule.push(errorMsg); return strategies[strategy].apply(dom, strategyAry); }) } }, start(){ for (var validatorFunc of this.cache) { var errorMsg = validatorFunc() // 開始校驗 if (errorMsg) { return errorMsg } } } }

目前為止,我們已經完成了當前的策略類書寫,接下來我們直接在場景中實際使用,

var numberDom = window.document.getElementsByTagName(input)[0];const validatorFunc = () => { var validator = new Validator(); validator.add(numberDom.value, [{ strategy: isNubmer, errorMsg: 請輸入數字 },{ strategy: minLength, errorMsg: 請輸入大於*數字 },{ strategy: maxLength, errorMsg: 請輸入小於*數字 },{ strategy: pointLength, errorMsg: 請輸入數字 }]);}numberDom.addEventListener(blur, function(){ let errorMsg = validatorFunc(); if (errorMsg) { console.log(errorMsg); return false; }})

備註:策略模式優勢是代碼更加優雅,可復用,書寫方便,但是也增加了代碼量和複雜度,有一定的門檻,所以還可以利用ES6的proxy對象,在對象訪問屬性時就進行攔截

第三種:ES6的proxy對象做屬性攔截

proxy相當於修改某些操作的默認行為,等同於在語言層面做出修改,所以屬於一種元編程,即對編程語言進行編程。理解成對目標對象的訪問都要經過這一層攔截,重構後代碼如下,具體的語法後面再解釋:

function validator(target, validator, errorMsg){ return new Proxy(target, { _validator: validator, set (target, key, value, proxy) { let errMsg = errorMsg; if (value == ) { return target[key] = false } let va = this._validator[key]; if (!!va(value)) { return Reflect.set(target, key, value, proxy); } else { return target[key] = false; } } })}

備註:proxy接收兩個參數,一個是目標對象,第二個是配置對象,對於每一個被代理的操作,需要提供一個對應的處理函數,該函數攔截對應的操作

注意:proxy起作用只有針對proxy實例,所以在實際運用中,重構代碼如下:

const errorMsg = {};const vali = validator({}, validators, errorMsg);var numberDom = window.document.getElementsByTagName(input)[0];numberDom.addEventListener(onBlur, function(){ let validatorNext = funciton*(){ yield vali.isNumber = numberDom.value; yield vali.minLength = numberDom.value; yield vali.maxLength = numberDom.value; yield vali.pointLength = numberDom.value; }; let validator = validatorNext(); validator.next(); ...}, false);

總結:上述幾種方案,各有各的優勢劣勢,前端代碼最終的方向是優雅,擴展性好,可復用,本例中具體的實現還需進一步驗證,接下來一節會專門抽個時間來proxy實現,有問題可及時私信知乎賬號

推薦閱讀:

Progressive Web App(技術周刊 2018-03-16)
如何搭建一個博客系統
前端日刊-2017.11.16
前端日刊-2018.02.11
前端日刊-2017.11.21

TAG:前端開發 | 前端工程師 |