策略模式定義了算法家族,分別封裝起來,讓他們之間可以互相替換,此模式讓算法的變化獨立於使用算飯的客戶.
先定義一個簡單的輸入表單:
<!DOCTYPE html> <html> <head> <meta charset="utf-"> <style> .form{ width: px; height: px; #margin: px auto; } .form-item-label{ width:px; text-align: right; float: left; } .form-item-input{ float: left; } .form-item{ width: % ; height: px; line-height: px; } </style> </head> <body> <div class='form'> <div class="form-item"> <div class='form-item-label'><span>用戶名:</span></div> <div class='form-item-input'><input id='userName' name='用戶名' type="text"></div> </div> <div class="form-item" > <div class='form-item-label'><span>密碼:</span></div> <div class='form-item-input'><input id='password' name='密碼' type="text"></div> </div> <div class="form-item" > <div class='form-item-label'><span>確認密碼:</span></div> <div class='form-item-input'><input id='repassword' name='密碼確認' type="text"></div> </div> <div class="form-item" > <div class='form-item-label'><span>郵箱:</span></div> <div class='form-item-input'><input id='mail' name='郵箱' type="text" ></div> </div> </div> <br> <button id='submit' >提交</button> <script type='text/javascript' src="../reference/jquery-...min.js"></script> </body> </html>
一般在頁面上編輯信息後的提交動作中,都需要對輸入的信息進行驗證,會看到把很多負責check的代碼寫在提交函數中或者寫在一個獨立的check函數中。
比如像下面這樣。
$(document).ready(function(){ $('#submit').bind('click', doSubmit); }); function doSubmit(){ var eleUserName = document.getElementById('userName'); if(eleUserName.value === '') { alert('用戶名不能為空'); return; } if(eleUserName.length < 6) { alert('用戶名長度不能少於6個字符'); return; } if(eleUserName.length > 6) { alert('用戶名長度不能多於20個字符'); return; } }
這樣的寫法功能上肯定能滿足要求,但是,會存在幾個問題:
1.如果我要在其他頁面上使用,那就要將代碼進行復制,所謂的復用就變成了復制,代碼會存在大量重復。好一點的會把check代碼分類整理封裝,單還會存在較多的重復復制。
2.如果我要增加一個輸入驗證,那麼就要直接修改提交函數,該函數會顯的臃腫,並且是破壞“開閉”原則的。
3.如果修改了提交函數,就要將函數設計的測試全都覆蓋一遍,因為,不知道何時就會發生誤改或者未知的情況。
改造步驟:
1.將每個驗證邏輯看成是一個驗證策略並封裝成每個驗證策略函數,函數參數保持一致,可以接受dom元素,被驗證的值,錯誤消息,定制參數。
2.定義驗證器,可將驗證策略函數導入,也可以添加。
3.驗證器提供驗證方法,用於驗證時的調用,其內部調用具體的驗證策略函數。
4.驗證調用。
步驟1.
把每一個if都看成一種校驗的業務規則,把每種業務規則作為一個單獨的策略函數,將所有的策略函數封裝成一個策略對象。
var validationStrategies = { isNoEmpty: function(element, errMsg, value) { if(value === '') { return this.buildInvalidObj(element, errMsg, value ); } }, minLength: function(element, errMsg, value, length) { if(value.length < length){ return this.buildInvalidObj(element, errMsg, value); } }, maxLength: function(element, errMsg, value, length) { if(value.length > length){ return this.buildInvalidObj(element, errMsg, value); } }, isMail: function(element, errMsg, value, length) { var reg = /^(\w-*\.*)+@(\w-?)+(\.\w{2,})+$/; if(!reg.test(value)){ return this.buildInvalidObj(element, errMsg, value); } } };
所有函數的參數的前3個都保持一致,而且是必須的,表示被驗證的DOM元素,錯誤消息,被驗證的值,第4個開始由函數自身的驗證規則決定定制的參數,可有多個參數。
“buildInvalidObj”方法只是把前3個參數打成一個錯誤對象進行返回,只要驗證不通過就會返回這個錯誤對象。
根據依賴倒置原則,高層次的模塊不應該依賴於低層次的模塊,因此不能讓驗證的調用方直接使用。
通過驗證器的方式進行封裝和抽象。
步驟2:
定義驗證器,可以將所有驗證策略導入其內,也可以單獨添加驗證策略函數。
//輸入驗證器 function InputValidators(){ this.validators = []; this.strategies = {}; } //從策略對象導入驗證策略函數 //參數: // strategies: 包含各種策略函數的對象 InputValidators.prototype.importStrategies = function(strategies) { for(var strategyName in strategies) { this.addValidationStrategy(strategyName, strategies[strategyName]); } }; //添加驗證策略函數 //參數: // name: 策略名稱 // strategy: 策略函數 InputValidators.prototype.addValidationStrategy = function(name, strategy){ this.strategies[name] = strategy; };
步驟3:
添加驗證方法,接受外部調用。
第一個參數rule,設置成驗證規則,比如 "minLength:6",通過下面的代碼會生成對具體策略函數的調用,調用會壓到緩存中,等待一起調用。
":6"表示策略函數根據自身規則所定制的參數。
//添加驗證方法 //參數: // rule: 驗證策略字符串 // element: 被驗證的dom元素 // errMsg: 驗證失敗時顯示的提示信息 // value: 被驗證的值 InputValidators.prototype.addValidator = function(rule, element, errMsg, value) { var that = this; var ruleElements = rule.split(":"); this.validators.push(function() { var strategy = ruleElements.shift(); var params = ruleElements; params.unshift(value); params.unshift(errMsg); params.unshift(element); return that.strategies[strategy].apply(that, params); }); };
通過一個check函數來調用所有的驗證。並將錯誤的結果進行返回。
//開始驗證 InputValidators.prototype.check = function() { for(var i = 0, validator; validator = this.validators[i++];){ var result = validator(); if(result) { return result; } } };
步驟4:
在需要驗證的地方,先new一個驗證器對象。
var validators = new InputValidators();
將包含驗證策略函數的對象導入,或者單獨添加驗證策略函數。
validators.importStrategies(validationStrategies); validators.addValidationStrategy('isEqual', function(element, errMsg, value1, value2) { if(value1 !== value2) { return this.buildInvalidObj(element, errMsg, value1 ); } });
可以看出,不同的驗證策略我們可以預先封裝進策略對象中,也可以根據實際情況即時添加。
然後通過添加驗證方法將需要驗證的策略,被驗證的dom元素,錯誤消息,被驗證的值添加進驗證器中,這樣避免了直接調用策略對象,降低了耦合性。
var eleUserName = document.getElementById('userName'); validators.addValidator('isNoEmpty', eleUserName, '用戶名不能為空', eleUserName.value); validators.addValidator('minLength:6', eleUserName, '用戶名的字符個數必須是6到20個', eleUserName.value); validators.addValidator('maxLength:20', eleUserName, '用戶名的字符個數必須是6到20個', eleUserName.value); var elePassword = document.getElementById('password'); validators.addValidator('isNoEmpty', elePassword, '密碼不能為空', elePassword.value); validators.addValidator('minLength:6', elePassword, '密碼的字符個數必須是6到20個', elePassword.value); validators.addValidator('maxLength:20', elePassword, '密碼的字符個數必須是6到20個', elePassword.value); var eleRepassword = document.getElementById('repassword'); validators.addValidator('isNoEmpty', eleRepassword, '確認密碼不能為空', eleRepassword.value); validators.addValidator('minLength:6', eleRepassword, '確認密碼的字符個數必須是6到20個', eleRepassword.value); validators.addValidator('maxLength:20', eleRepassword, '確認密碼的字符個數必須是6到20個', eleRepassword.value); validators.addValidator('isEqual:' + elePassword.value, eleRepassword, '兩次密碼不一致', eleRepassword.value); var eleMail = document.getElementById('mail'); validators.addValidator('isNoEmpty', eleMail, '郵箱不能為空', eleMail.value); validators.addValidator('isMail', eleMail, '郵箱不是一個有效的格式', eleMail.value);
調用驗證器的check執行所有的驗證。
var result = validators.check(); if(result){ alert(result.errMsg); result.element.focus(); result.element.select(); return false; }
check返回的是錯誤對象,我們可以在check後通過該對象統一地對DOM元素進行提示性操作,比如設置焦點,選中內容,或者為輸入框外部包上一層紅色的樣式。
至此,可以看出通過策略模式的改在,輸入驗證時,我們只需要關心用哪個驗證規則,采用什麼樣的提示性信息即可,不再暴露實現細節,方便調用,方便後續的擴展和組件化。
全部代碼:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <style> .form{ width: 400px; height: 200px; #margin: 0px auto; } .form-item-label{ width:100px; text-align: right; float: left; } .form-item-input{ float: left; } .form-item{ width: 100% ; height: 50px; line-height: 50px; } </style> </head> <body> <div class='form'> <div class="form-item"> <div class='form-item-label'><span>用戶名:</span></div> <div class='form-item-input'><input id='userName' name='用戶名' type="text"></div> </div> <div class="form-item" > <div class='form-item-label'><span>密碼:</span></div> <div class='form-item-input'><input id='password' name='密碼' type="text"></div> </div> <div class="form-item" > <div class='form-item-label'><span>確認密碼:</span></div> <div class='form-item-input'><input id='repassword' name='密碼確認' type="text"></div> </div> <div class="form-item" > <div class='form-item-label'><span>郵箱:</span></div> <div class='form-item-input'><input id='mail' name='郵箱' type="text" ></div> </div> </div> <br> <button id='submit' >提交</button> <script type='text/javascript' src="../reference/jquery-1.11.3.min.js"></script> <script type='text/javascript'> $(document).ready(function(){ $('#submit').bind('click', doSubmit); }); function doSubmit(){ var validators = new InputValidators(); validators.importStrategies(validationStrategies); validators.addValidationStrategy('isEqual', function(element, errMsg, value1, value2) { if(value1 !== value2) { return this.buildInvalidObj(element, errMsg, value1 ); } }); var eleUserName = document.getElementById('userName'); validators.addValidator('isNoEmpty', eleUserName, '用戶名不能為空', eleUserName.value); validators.addValidator('minLength:6', eleUserName, '用戶名的字符個數必須是6到20個', eleUserName.value); validators.addValidator('maxLength:20', eleUserName, '用戶名的字符個數必須是6到20個', eleUserName.value); var elePassword = document.getElementById('password'); validators.addValidator('isNoEmpty', elePassword, '密碼不能為空', elePassword.value); validators.addValidator('minLength:6', elePassword, '密碼的字符個數必須是6到20個', elePassword.value); validators.addValidator('maxLength:20', elePassword, '密碼的字符個數必須是6到20個', elePassword.value); var eleRepassword = document.getElementById('repassword'); validators.addValidator('isNoEmpty', eleRepassword, '確認密碼不能為空', eleRepassword.value); validators.addValidator('minLength:6', eleRepassword, '確認密碼的字符個數必須是6到20個', eleRepassword.value); validators.addValidator('maxLength:20', eleRepassword, '確認密碼的字符個數必須是6到20個', eleRepassword.value); validators.addValidator('isEqual:' + elePassword.value, eleRepassword, '兩次密碼不一致', eleRepassword.value); var eleMail = document.getElementById('mail'); validators.addValidator('isNoEmpty', eleMail, '郵箱不能為空', eleMail.value); validators.addValidator('isMail', eleMail, '郵箱不是一個有效的格式', eleMail.value); var result = validators.check(); if(result){ alert(result.errMsg); result.element.focus(); result.element.select(); return false; } alert('驗證通過'); } //輸入驗證器 function InputValidators(){ this.validators = []; this.strategies = {}; //this.from(validationStrategies); } //添加驗證方法 //參數: // rule: 驗證策略字符串 // element: 被驗證的dom元素 // errMsg: 驗證失敗時顯示的提示信息 // value: 被驗證的值 InputValidators.prototype.addValidator = function(rule, element, errMsg, value) { var that = this; var ruleElements = rule.split(":"); this.validators.push(function() { var strategy = ruleElements.shift(); var params = ruleElements; params.unshift(value); params.unshift(errMsg); params.unshift(element); return that.strategies[strategy].apply(that, params); }); }; //添加驗證策略函數 //參數: // name: 策略名稱 // strategy: 策略函數 InputValidators.prototype.addValidationStrategy = function(name, strategy){ this.strategies[name] = strategy; }; //從策略對象導入驗證策略函數 //參數: // strategies: 包含各種策略函數的對象 InputValidators.prototype.importStrategies = function(strategies) { for(var strategyName in strategies) { this.addValidationStrategy(strategyName, strategies[strategyName]); } }; //驗證失敗時,將相關的錯誤信息打包返回 //參數: // element: dom元素 // errMsg: 驗證失敗時的提示消息 // value: 被驗證的值 InputValidators.prototype.buildInvalidObj = function(element, errMsg, value){ return { 'value': value, 'element': element, 'errMsg': errMsg }; }; //開始驗證 InputValidators.prototype.check = function() { for(var i = 0, validator; validator = this.validators[i++];){ var result = validator(); if(result) { return result; } } }; //驗證策略對象,包含默認的驗證策略函數 var validationStrategies = { isNoEmpty: function(element, errMsg, value) { if(value === '') { return this.buildInvalidObj(element, errMsg, value ); } }, minLength: function(element, errMsg, value, length) { if(value.length < length){ return this.buildInvalidObj(element, errMsg, value); } }, maxLength: function(element, errMsg, value, length) { if(value.length > length){ return this.buildInvalidObj(element, errMsg, value); } }, isMail: function(element, errMsg, value, length) { var reg = /^(\w-*\.*)+@(\w-?)+(\.\w{2,})+$/; if(!reg.test(value)){ return this.buildInvalidObj(element, errMsg, value); } } }; </script> </body> </html>
以上所述是小編給大家介紹的javascript設計模式--策略模式之輸入驗證的全部內容,希望大家喜歡。