javascript设计模式--策略模式之输入验证
策略模式定义了算法家族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算饭的客户.
先定义一个简单的输入表单:
<!DOCTYPEhtml> <html> <head> <metacharset="utf-"> <style> .form{ width:px; height:px; #margin:pxauto; } .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> <divclass='form'> <divclass="form-item"> <divclass='form-item-label'><span>用户名:</span></div> <divclass='form-item-input'><inputid='userName'name='用户名'type="text"></div> </div> <divclass="form-item"> <divclass='form-item-label'><span>密码:</span></div> <divclass='form-item-input'><inputid='password'name='密码'type="text"></div> </div> <divclass="form-item"> <divclass='form-item-label'><span>确认密码:</span></div> <divclass='form-item-input'><inputid='repassword'name='密码确认'type="text"></div> </div> <divclass="form-item"> <divclass='form-item-label'><span>邮箱:</span></div> <divclass='form-item-input'><inputid='mail'name='邮箱'type="text"></div> </div> </div> <br> <buttonid='submit'>提交</button> <scripttype='text/javascript'src="../reference/jquery-...min.js"></script> </body> </html>
一般在页面上编辑信息后的提交动作中,都需要对输入的信息进行验证,会看到把很多负责check的代码写在提交函数中或者写在一个独立的check函数中。
比如像下面这样。
$(document).ready(function(){ $('#submit').bind('click',doSubmit); }); functiondoSubmit(){ vareleUserName=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都看成一种校验的业务规则,把每种业务规则作为一个单独的策略函数,将所有的策略函数封装成一个策略对象。
varvalidationStrategies={ isNoEmpty:function(element,errMsg,value){ if(value===''){ returnthis.buildInvalidObj(element,errMsg,value); } }, minLength:function(element,errMsg,value,length){ if(value.length<length){ returnthis.buildInvalidObj(element,errMsg,value); } }, maxLength:function(element,errMsg,value,length){ if(value.length>length){ returnthis.buildInvalidObj(element,errMsg,value); } }, isMail:function(element,errMsg,value,length){ varreg=/^(\w-*\.*)+@(\w-?)+(\.\w{2,})+$/; if(!reg.test(value)){ returnthis.buildInvalidObj(element,errMsg,value); } } };
所有函数的参数的前3个都保持一致,而且是必须的,表示被验证的DOM元素,错误消息,被验证的值,第4个开始由函数自身的验证规则决定定制的参数,可有多个参数。
“buildInvalidObj”方法只是把前3个参数打成一个错误对象进行返回,只要验证不通过就会返回这个错误对象。
根据依赖倒置原则,高层次的模块不应该依赖于低层次的模块,因此不能让验证的调用方直接使用。
通过验证器的方式进行封装和抽象。
步骤2:
定义验证器,可以将所有验证策略导入其内,也可以单独添加验证策略函数。
//输入验证器 functionInputValidators(){ this.validators=[]; this.strategies={}; } //从策略对象导入验证策略函数 //参数: //strategies:包含各种策略函数的对象 InputValidators.prototype.importStrategies=function(strategies){ for(varstrategyNameinstrategies){ 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){ varthat=this; varruleElements=rule.split(":"); this.validators.push(function(){ varstrategy=ruleElements.shift(); varparams=ruleElements; params.unshift(value); params.unshift(errMsg); params.unshift(element); returnthat.strategies[strategy].apply(that,params); }); };
通过一个check函数来调用所有的验证。并将错误的结果进行返回。
//开始验证 InputValidators.prototype.check=function(){ for(vari=0,validator;validator=this.validators[i++];){ varresult=validator(); if(result){ returnresult; } } };
步骤4:
在需要验证的地方,先new一个验证器对象。
varvalidators=newInputValidators();
将包含验证策略函数的对象导入,或者单独添加验证策略函数。
validators.importStrategies(validationStrategies); validators.addValidationStrategy('isEqual',function(element,errMsg,value1,value2){ if(value1!==value2){ returnthis.buildInvalidObj(element,errMsg,value1); } });
可以看出,不同的验证策略我们可以预先封装进策略对象中,也可以根据实际情况即时添加。
然后通过添加验证方法将需要验证的策略,被验证的dom元素,错误消息,被验证的值添加进验证器中,这样避免了直接调用策略对象,降低了耦合性。
vareleUserName=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); varelePassword=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); vareleRepassword=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); vareleMail=document.getElementById('mail'); validators.addValidator('isNoEmpty',eleMail,'邮箱不能为空',eleMail.value); validators.addValidator('isMail',eleMail,'邮箱不是一个有效的格式',eleMail.value);
调用验证器的check执行所有的验证。
varresult=validators.check(); if(result){ alert(result.errMsg); result.element.focus(); result.element.select(); returnfalse; }
check返回的是错误对象,我们可以在check后通过该对象统一地对DOM元素进行提示性操作,比如设置焦点,选中内容,或者为输入框外部包上一层红色的样式。
至此,可以看出通过策略模式的改在,输入验证时,我们只需要关心用哪个验证规则,采用什么样的提示性信息即可,不再暴露实现细节,方便调用,方便后续的扩展和组件化。
全部代码:
<!DOCTYPEhtml> <html> <head> <metacharset="utf-8"> <style> .form{ width:400px; height:200px; #margin:0pxauto; } .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> <divclass='form'> <divclass="form-item"> <divclass='form-item-label'><span>用户名:</span></div> <divclass='form-item-input'><inputid='userName'name='用户名'type="text"></div> </div> <divclass="form-item"> <divclass='form-item-label'><span>密码:</span></div> <divclass='form-item-input'><inputid='password'name='密码'type="text"></div> </div> <divclass="form-item"> <divclass='form-item-label'><span>确认密码:</span></div> <divclass='form-item-input'><inputid='repassword'name='密码确认'type="text"></div> </div> <divclass="form-item"> <divclass='form-item-label'><span>邮箱:</span></div> <divclass='form-item-input'><inputid='mail'name='邮箱'type="text"></div> </div> </div> <br> <buttonid='submit'>提交</button> <scripttype='text/javascript'src="../reference/jquery-1.11.3.min.js"></script> <scripttype='text/javascript'> $(document).ready(function(){ $('#submit').bind('click',doSubmit); }); functiondoSubmit(){ varvalidators=newInputValidators(); validators.importStrategies(validationStrategies); validators.addValidationStrategy('isEqual',function(element,errMsg,value1,value2){ if(value1!==value2){ returnthis.buildInvalidObj(element,errMsg,value1); } }); vareleUserName=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); varelePassword=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); vareleRepassword=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); vareleMail=document.getElementById('mail'); validators.addValidator('isNoEmpty',eleMail,'邮箱不能为空',eleMail.value); validators.addValidator('isMail',eleMail,'邮箱不是一个有效的格式',eleMail.value); varresult=validators.check(); if(result){ alert(result.errMsg); result.element.focus(); result.element.select(); returnfalse; } alert('验证通过'); } //输入验证器 functionInputValidators(){ this.validators=[]; this.strategies={}; //this.from(validationStrategies); } //添加验证方法 //参数: //rule:验证策略字符串 //element:被验证的dom元素 //errMsg:验证失败时显示的提示信息 //value:被验证的值 InputValidators.prototype.addValidator=function(rule,element,errMsg,value){ varthat=this; varruleElements=rule.split(":"); this.validators.push(function(){ varstrategy=ruleElements.shift(); varparams=ruleElements; params.unshift(value); params.unshift(errMsg); params.unshift(element); returnthat.strategies[strategy].apply(that,params); }); }; //添加验证策略函数 //参数: //name:策略名称 //strategy:策略函数 InputValidators.prototype.addValidationStrategy=function(name,strategy){ this.strategies[name]=strategy; }; //从策略对象导入验证策略函数 //参数: //strategies:包含各种策略函数的对象 InputValidators.prototype.importStrategies=function(strategies){ for(varstrategyNameinstrategies){ 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(vari=0,validator;validator=this.validators[i++];){ varresult=validator(); if(result){ returnresult; } } }; //验证策略对象,包含默认的验证策略函数 varvalidationStrategies={ isNoEmpty:function(element,errMsg,value){ if(value===''){ returnthis.buildInvalidObj(element,errMsg,value); } }, minLength:function(element,errMsg,value,length){ if(value.length<length){ returnthis.buildInvalidObj(element,errMsg,value); } }, maxLength:function(element,errMsg,value,length){ if(value.length>length){ returnthis.buildInvalidObj(element,errMsg,value); } }, isMail:function(element,errMsg,value,length){ varreg=/^(\w-*\.*)+@(\w-?)+(\.\w{2,})+$/; if(!reg.test(value)){ returnthis.buildInvalidObj(element,errMsg,value); } } }; </script> </body> </html>
以上所述是小编给大家介绍的javascript设计模式--策略模式之输入验证的全部内容,希望大家喜欢。