javascript設(shè)計模式--策略模式之輸入驗證
策略模式定義了算法家族,分別封裝起來,讓他們之間可以互相替換,此模式讓算法的變化獨立于使用算飯的客戶.
先定義一個簡單的輸入表單:
<!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的代碼寫在提交函數(shù)中或者寫在一個獨立的check函數(shù)中。
比如像下面這樣。
$(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.如果我要在其他頁面上使用,那就要將代碼進行復(fù)制,所謂的復(fù)用就變成了復(fù)制,代碼會存在大量重復(fù)。好一點的會把check代碼分類整理封裝,單還會存在較多的重復(fù)復(fù)制。
2.如果我要增加一個輸入驗證,那么就要直接修改提交函數(shù),該函數(shù)會顯的臃腫,并且是破壞“開閉”原則的。
3.如果修改了提交函數(shù),就要將函數(shù)設(shè)計的測試全都覆蓋一遍,因為,不知道何時就會發(fā)生誤改或者未知的情況。
改造步驟:
1.將每個驗證邏輯看成是一個驗證策略并封裝成每個驗證策略函數(shù),函數(shù)參數(shù)保持一致,可以接受dom元素,被驗證的值,錯誤消息,定制參數(shù)。
2.定義驗證器,可將驗證策略函數(shù)導入,也可以添加。
3.驗證器提供驗證方法,用于驗證時的調(diào)用,其內(nèi)部調(diào)用具體的驗證策略函數(shù)。
4.驗證調(diào)用。
步驟1.
把每一個if都看成一種校驗的業(yè)務(wù)規(guī)則,把每種業(yè)務(wù)規(guī)則作為一個單獨的策略函數(shù),將所有的策略函數(shù)封裝成一個策略對象。
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);
}
}
};
所有函數(shù)的參數(shù)的前3個都保持一致,而且是必須的,表示被驗證的DOM元素,錯誤消息,被驗證的值,第4個開始由函數(shù)自身的驗證規(guī)則決定定制的參數(shù),可有多個參數(shù)。
“buildInvalidObj”方法只是把前3個參數(shù)打成一個錯誤對象進行返回,只要驗證不通過就會返回這個錯誤對象。
根據(jù)依賴倒置原則,高層次的模塊不應(yīng)該依賴于低層次的模塊,因此不能讓驗證的調(diào)用方直接使用。
通過驗證器的方式進行封裝和抽象。
步驟2:
定義驗證器,可以將所有驗證策略導入其內(nèi),也可以單獨添加驗證策略函數(shù)。
//輸入驗證器
function InputValidators(){
this.validators = [];
this.strategies = {};
}
//從策略對象導入驗證策略函數(shù)
//參數(shù):
// strategies: 包含各種策略函數(shù)的對象
InputValidators.prototype.importStrategies = function(strategies) {
for(var strategyName in strategies) {
this.addValidationStrategy(strategyName, strategies[strategyName]);
}
};
//添加驗證策略函數(shù)
//參數(shù):
// name: 策略名稱
// strategy: 策略函數(shù)
InputValidators.prototype.addValidationStrategy = function(name, strategy){
this.strategies[name] = strategy;
};
步驟3:
添加驗證方法,接受外部調(diào)用。
第一個參數(shù)rule,設(shè)置成驗證規(guī)則,比如 "minLength:6",通過下面的代碼會生成對具體策略函數(shù)的調(diào)用,調(diào)用會壓到緩存中,等待一起調(diào)用。
":6"表示策略函數(shù)根據(jù)自身規(guī)則所定制的參數(shù)。
//添加驗證方法
//參數(shù):
// 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函數(shù)來調(diào)用所有的驗證。并將錯誤的結(jié)果進行返回。
//開始驗證
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();
將包含驗證策略函數(shù)的對象導入,或者單獨添加驗證策略函數(shù)。
validators.importStrategies(validationStrategies);
validators.addValidationStrategy('isEqual', function(element, errMsg, value1, value2) {
if(value1 !== value2) {
return this.buildInvalidObj(element, errMsg, value1 );
}
});
可以看出,不同的驗證策略我們可以預(yù)先封裝進策略對象中,也可以根據(jù)實際情況即時添加。
然后通過添加驗證方法將需要驗證的策略,被驗證的dom元素,錯誤消息,被驗證的值添加進驗證器中,這樣避免了直接調(diào)用策略對象,降低了耦合性。
var eleUserName = document.getElementById('userName');
validators.addValidator('isNoEmpty', eleUserName, '用戶名不能為空', eleUserName.value);
validators.addValidator('minLength:6', eleUserName, '用戶名的字符個數(shù)必須是6到20個', eleUserName.value);
validators.addValidator('maxLength:20', eleUserName, '用戶名的字符個數(shù)必須是6到20個', eleUserName.value);
var elePassword = document.getElementById('password');
validators.addValidator('isNoEmpty', elePassword, '密碼不能為空', elePassword.value);
validators.addValidator('minLength:6', elePassword, '密碼的字符個數(shù)必須是6到20個', elePassword.value);
validators.addValidator('maxLength:20', elePassword, '密碼的字符個數(shù)必須是6到20個', elePassword.value);
var eleRepassword = document.getElementById('repassword');
validators.addValidator('isNoEmpty', eleRepassword, '確認密碼不能為空', eleRepassword.value);
validators.addValidator('minLength:6', eleRepassword, '確認密碼的字符個數(shù)必須是6到20個', eleRepassword.value);
validators.addValidator('maxLength:20', eleRepassword, '確認密碼的字符個數(shù)必須是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);
調(diào)用驗證器的check執(zhí)行所有的驗證。
var result = validators.check();
if(result){
alert(result.errMsg);
result.element.focus();
result.element.select();
return false;
}
check返回的是錯誤對象,我們可以在check后通過該對象統(tǒng)一地對DOM元素進行提示性操作,比如設(shè)置焦點,選中內(nèi)容,或者為輸入框外部包上一層紅色的樣式。
至此,可以看出通過策略模式的改在,輸入驗證時,我們只需要關(guān)心用哪個驗證規(guī)則,采用什么樣的提示性信息即可,不再暴露實現(xiàn)細節(jié),方便調(diào)用,方便后續(xù)的擴展和組件化。
全部代碼:
<!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, '用戶名的字符個數(shù)必須是6到20個', eleUserName.value);
validators.addValidator('maxLength:20', eleUserName, '用戶名的字符個數(shù)必須是6到20個', eleUserName.value);
var elePassword = document.getElementById('password');
validators.addValidator('isNoEmpty', elePassword, '密碼不能為空', elePassword.value);
validators.addValidator('minLength:6', elePassword, '密碼的字符個數(shù)必須是6到20個', elePassword.value);
validators.addValidator('maxLength:20', elePassword, '密碼的字符個數(shù)必須是6到20個', elePassword.value);
var eleRepassword = document.getElementById('repassword');
validators.addValidator('isNoEmpty', eleRepassword, '確認密碼不能為空', eleRepassword.value);
validators.addValidator('minLength:6', eleRepassword, '確認密碼的字符個數(shù)必須是6到20個', eleRepassword.value);
validators.addValidator('maxLength:20', eleRepassword, '確認密碼的字符個數(shù)必須是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);
}
//添加驗證方法
//參數(shù):
// 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);
});
};
//添加驗證策略函數(shù)
//參數(shù):
// name: 策略名稱
// strategy: 策略函數(shù)
InputValidators.prototype.addValidationStrategy = function(name, strategy){
this.strategies[name] = strategy;
};
//從策略對象導入驗證策略函數(shù)
//參數(shù):
// strategies: 包含各種策略函數(shù)的對象
InputValidators.prototype.importStrategies = function(strategies) {
for(var strategyName in strategies) {
this.addValidationStrategy(strategyName, strategies[strategyName]);
}
};
//驗證失敗時,將相關(guān)的錯誤信息打包返回
//參數(shù):
// 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;
}
}
};
//驗證策略對象,包含默認的驗證策略函數(shù)
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設(shè)計模式--策略模式之輸入驗證的全部內(nèi)容,希望大家喜歡。
相關(guān)文章
js中setTimeout的妙用--防止循環(huán)超時
本文主要介紹了使用setTimeout實現(xiàn)防止循環(huán)超時的方法,具有很好的參考價值。下面跟著小編一起來看下吧2017-03-03
javascript實現(xiàn)博客園頁面右下角返回頂部按鈕
這篇文章主要介紹了使用javascript實現(xiàn)博客園頁面右下角返回頂部按鈕的思路及源碼,非常不錯,這里推薦給小伙伴們2015-02-02

