策略模式是指定义一系列的算法,把它们一个个封装起来,并且使它们可以互相替换。
本案例使用策略模式来封装表单校验。
假设我们正在编写一个注册的页面,在点击注册按钮之前,有如下几条校验逻辑。
1.用户名不能为空
2.密码长度不能少于6位
3.手机号码必须符合格式
表单校验的第一个版本
1 2 3 4 5 6
| <form action="" id="registerForm" method="post"> 请输入用户名:<input type="text" name="userName"/> 请输入密码:<input type="password" name="passWord" /> 请输入手机号:<input type="text" name="phoneNumber"/> <button>提交</button> </form>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| var registerForm = document.getElementById('registerForm'); registerForm.onsubmit = function(){ if(registerForm.userName.value === ''){ alert('用户名不能为空'); return false; } if(registerForm.password.value.length<6){ alert('密码长度不能少于6位'); return false; } if(!/(^1[3|5|8][0-9]$)/.test(registerForm.phoneNumber.value)){ alert('手机号码格式不正确'); return false; } }
|
这是一种很常见的代码编写方式,它的缺点如下:
1.registerForm.onsubmit函数比较庞大,包含了很多if-else语句,这些语句需要覆盖所有的校验规则
2.registerForm.onsubmit函数缺乏弹性,如果增加了一种新的校验规则,或者想把密码的长度校验从6改成8,我们都必须深入函数的内部实现,这是违反开放-封闭原则的。
3.算法的复用性差,如果在程序中增加了另外一个表单,这个表单也需要进行一些类似的校验。
用策略模式重构表单校验
首先我们先把这些校验逻辑都封装成策略对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| var strategies = { isNonEmpty:function(value,errorMsg){ if( value === ''){ return errorMsg; } }, minLength:function(value,length,errorMsg){ if(value.length < length){ return errorMsg; } }, isMobile:function(value,errorMsg){ if(!/(^1[3|5|8][0-9]{9}/.test(value)){ return errorMsg; } } };
|
接下来我们准备实现Validator类,Validator类在这里作为Context,负责接收用户的请求并委托给strategy对象,在实现该类之前,我们先要提前了解用户是如何向Validator类发送请求的,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| var validataFunc = function(){ var validator = new Validator(); validator.add(registerForm.userName,'isNonEmpty','用户名不能为空'); validator.add(registerForm.passWord,'minLength:6','密码长度不能少于6位'); validator.add(registerForm.phoneNumber,'isMobile','手机号码格式不正确'); var errorMsg = validator.start(); return errorMsg; }; var registerForm = document.getElementById('registerForm'); registerForm.onsubmit = function(){ var errorMsg = validataFunc(); if(errorMsg){ alert(errorMsg); return false; } };
|
最后是Validator类的实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| var Validator = function(){ this.cache = []; }; Validator.prototype.add = function(dom,rule,errorMsg){ var ary = rule.split(':'); this.cache.push(function(){ var strategy = ary.shift(); ary.unshift(dom.value); ary.push(errorMsg); return strategies[strategy].apply(dom,ary); }); }; Validator.prototype.start = function(){ for (var i =0,validatorFunc;validatorFunc = this.cache[i++];) { var msg = validatorFunc(); if(msg){ return msg; } } }
|
以上就是策略模式的实现,然而还有一些小遗憾,一个文本输入框只能对应一种校验规则,比如,用户名输入框只能校验输入是否为空。
如果我们既想校验是否为空,又想校验输入文本的长度不小于10,我们期望以这样的形式进行校验。
1 2 3 4 5 6 7
| validator.add(registerForm.userName,[{ strategy: 'isNonEmpty', errorMsg:'用户名不能为空' },{ strategy:'minLength:10', errorMsg:'用户名长度不能小于10位' }]);
|
下面提供的代码可用于一个文本输入框对应多种校验规则:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
| var strategies = { isNonEmpty:function(value,errorMsg){ if( value === ''){ return errorMsg; } }, minLength:function(value,length,errorMsg){ if(value.length < length){ return errorMsg; } }, isMobile:function(value,errorMsg){ if(!/(^1[3|5|8][0-9]{9}/.test(value)){ return errorMsg; } } }; var Validator = function(){ this.cache = []; }; Validator.prototype.add = function(dom, rules){ var self = this; for (var i = 0,rule;rule = rules[i++];) { (function(rule){ var strategyAry = rule.strategy.split(':'); var errorMsg = rule.errorMsg; self.cache.push(function(){ var strategy = strategyAry.shift(); strategyAry.unshift(dom.value); strategyAry.push(errorMsg); return strategies[strategy].apply(dom,strategyAry); }); })(rule) } }; Validator.prototype.start = function(){ for (var i =0,validatorFunc;validatorFunc = this.cache[i++];) { var errorMsg = validatorFunc(); if(errorMsg){ return errorMsg; } } }; var registerForm = document.getElementById('registerForm'); var validataFunc = function(){ var validator = new Validator(); validator.add(registerForm.userName,[{ strategy: 'isNonEmpty', errorMsg:'用户名不能为空' },{ strategy:'minLength:10', errorMsg:'用户名长度不能小于10位' }]); validator.add(registerForm.passWord,[{ strategy: 'minLength:6', errorMsg:'密码长度不能小于6位' }]); validator.add(registerForm.phoneNumber,[{ strategy: 'isMobile', errorMsg:'手机号码格式不正确' }]); var errorMsg = validator.start(); return errorMsg; } registerForm.onsubmit = function(){ var errorMsg = validataFunc(); if(errorMsg){ alert(errorMsg); return false; } }
|
本文参考:
《javascript设计模式与开发实践》