原始验证
现在我们有一个表单:
<form action="/" id="myForm" method="POST">
用户名:<input name='user' type="text">
密 码 :<input name='pwd' type="text">
<button type="submit" name='btn'>提交表单</button>
</form>
我们要做表单验证,那么常见的写法是下面这种if else分支处理
<script>
var form = document.getElementById('myForm');
form.btn.onclick = function(e) {
var userVal = form.user.value;
var pwdVal = form.pwd.value;
if (!userVal) {
console.log('用户名不能为空');
return false;
}
if (!pwdVal || (pwdVal.length <= 6)) {
console.log('密码长度必须大于6位');
return false;
}
console.log('提交成功');
return false;
}
</script>
但是这种写法有个很大的缺点,就是不具备扩展性。如果要多一个手机号的验证,那么就要加多一个else分支做业务处理。而且写完的验证也没办法应用到别的表单上,十分不方便。
使用策略模式验证
为了可以实现动态认证,并且提高代码扩展性,我们可以采用策略模式来改写表单验证
第一步:定义策略对象
//策略对象
var strategy = {
require: function(val, msg) {
if (!val) {
return msg
}
},
min: function(val, length, msg) {
if (!val || (val.length <= 6)) {
return msg
}
},
phone: function(val, msg) {
let patt = /^1[3-9]\d{9}$/
let flag = patt.test(val)
if (!flag) {
return msg
}
}
}
定义策略对象的目的是抽象、封装每个验证算法的业务逻辑。每添加一种验证,其实就是新增一种策略而已,可直接在策略对象中添加新方法,方便扩展。抽象是为了能让方法能适应多更的应用场景,比如min方法中的length可以指定任意长度的字符。
第二部:定义策略类
//策略类
var Validate = function() {
this.rules = []
this.add = function(dom, rule, msg) {
let args = rule.split(':')
let func = args.shift();
args.push(msg)
args.unshift(dom.value)
let obj = {
func,
args
}
this.rules.push(obj)
//strategy[func]
}
this.check = function() {
var errors = []
for (var i = 0; i < this.rules.length; i++) {
var error = strategy[this.rules[i].func].apply(strategy, this.rules[i].args) //调用策略对象中的方法
if (error) {
errors.push(error)
}
}
return errors
}
}
我们定义了一个rules属性,一个add方法,一个check方法。rules属性是记录add方法中增加的验证规则,check方法是将rules中的验证规则循环调用策略对象中的方法。这样一来可以实现策略对象的高可用。
调用
function checkForm() {
var validate = new Validate()
validate.add(form.user, 'require', '用户名必须填写')
validate.add(form.pwd, 'min:6', '密码长度必须大于6位')
var res = validate.check()
if (res.length) {
console.log(res)
} else {
console.log('验证成功')
}
return false;
}
var form = document.getElementById('myForm')
form.btn.onclick = checkForm
可见,使用策略模式之后,调用验证变得十分简洁。而且还可用于其他文件,完全可以将其封装成类库来使用。
完整代码:
//策略对象
var strategy = {
require: function(val, msg) {
if (!val) {
return msg
}
},
min: function(val, length, msg) {
if (!val || (val.length <= 6)) {
return msg
}
},
}
//策略类
var Validate = function() {
this.rules = []
this.add = function(dom, rule, msg) {
let args = rule.split(':')
let func = args.shift();
args.push(msg)
args.unshift(dom.value)
let obj = {
func,
args
}
this.rules.push(obj)
//strategy[func]
}
this.check = function() {
var errors = []
for (var i = 0; i < this.rules.length; i++) {
var error = strategy[this.rules[i].func].apply(strategy, this.rules[i].args) //调用策略对象中的方法
if (error) {
errors.push(error)
}
}
return errors
}
}
//调用
function checkForm() {
var validate = new Validate()
validate.add(form.user, 'require', '用户名必须填写')
validate.add(form.pwd, 'min:6', '密码长度必须大于6位')
var res = validate.check()
if (res.length) {
console.log(res)
} else {
console.log('验证成功')
}
return false;
}
var form = document.getElementById('myForm')
form.btn.onclick = checkForm
我们再来看看扩展性如何。如果我们要多添加一条手机号的,那么只需要在策略对象中添加相应的方法即可:
成功运行。
可以看到改写后的代码扩展性也不错,而且使用起来更加简单方便。
总结
在某些分支众多,业务逻辑需要频繁if else判断的场景下,可以考虑抽象封装策略模式来完成工作任务,使得代码更加容易扩展,适用性更强