啥时策略模式?
举个例子,假如你从北京到杭州,可以选择坐飞机去,还可以坐汽车去,还可以自驾去。
每个方法都可以去。
策略模式就是实现一个任务有多个方法,这些方法都可以相互替换。把这些方法封装起来就是策略模式。
再举个例子,年终奖发放,绩效s的年终奖是工资的4倍。绩效A的年终奖是工资的3倍。绩效B的年终奖是工资的2倍。
js实现
var strategies={
S: function (salary) {
return salary * 4
},
A: function (salary) {
return salary * 3
},
B: function (salary) {
return salary * 2
}
}
var caculateBouns = function (level,salary) {
return strategies[level](salary)
}
console.log(caculateBouns('S',2000))
console.log(caculateBouns('A',3000))
console.log(caculateBouns('B',4000))
从定义上来看,策略模式就是用来封装算法的。
来点实用的。常见的表单验证如何用到策略模式呢?
假设我们正在编写一个用户注册的页面,在点击注册按钮之前,有以下几条校验规则。
1 :用户名不能为空
2 :密码长度不能少于6位
3: 手机号码必须符合格式
var registForm = document.getElementById('registForm');
registForm.onSubmit = function(){
if(registForm.userName.value === ''){
alert('用户名不能为空')
return false
}
if(registForm.password.value.length < 6){
alert('密码长度不能少于6位')
return false
}
if(!/^1[3|5|8|7]\d{9}$/.test(registForm.phoneNumber.value)){
alert('手机格式不正确')
return false
}
}
是不是你经常这么写?我也经常这么写。要想成为一名更优秀的程序员就要时刻反思自己的代码
那么这段代码有什么缺点呢??
1 registForm.onsubmit函数比较庞大,包含了很多if语句
2 函数缺乏弹性,如果新增一条校验规则,或者把密码长度从6改为8,我们就必须修改函数,这与开放-封闭原则相违背。
开放是对扩展开放,扩展功能是可以的。封闭是对修改封闭,对函数的内部修改是不提倡的,尽量不要去修改写过的代码。
3 算法的复用性差,如果项目中新增了另一个表单,这个表单也需要进行一些类似的校验。那我们就只能复制这段代码,这显然不是上策。
使用策略模式来优化以上代码
首先我们需要把这些校验规则封装成策略对象
// 策略类(包含哪些校验规则)
var strategies = {
isNotEmpty: 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|8|9|5]\d{9}$/.test(value)){
return errorMsg
}
}
}
// 校验容器
var Validator = function () {
this.cache = []; // 缓存方法
}
validator.prototype.add=function(dom,rules){
var self = this;
for(let i =0,rule;rule = rules[i++]){
let strategyAry = rule.strategy.split(':');
let errorMsg = rule.errorMsg;
self.cache.push(function(){
var strategy = strategyAry.shift(); // 方法用于把数组的第一个元素从其中删除,并返回第一个元素的值。
startegyAry.unshift(dom.value) // 从头加
startegyAry.push(errorMsg) // 从尾部加 [value,length,errorMsg]
return strategies[strategy].apply(dom,startegyAry)
})
}
}
Validator.prototype.start = function (){
for(let i = 0,validatorFun;validatorFun = this.cache[i++]){
var errMsg = validatorFun()
if(errMsg){
return errMsg
}
}
}
// 客户端调用
var registForm = document.getElementById('registForm')
var validatorFun = function(){
var validator = new Validator()
validator.add(registForm.userName,[{
startegy: 'isNotEmpty',
errorMsg: '用户名不能为空'
},{
strategy: 'minLength: 10',
errorMsg: '用户名长度不能小于10'
}])
validator.add(registForm.password,[{
strategy: 'minLength:8',
errorMsg: '密码不能少于6'
}])
validator.add(registForm.phoneNumber,[{
strategy: 'isMobile',
errorMsg: '手机格式不正确'
}])
var errorMsg = validator.start();
return errorMsg
}
registForm.onSubmit= function(){
var errMsg = validatorFun();
if(errMsg){
alert(errMsg)
return false
}
}
这样一来,校验规则只可以扩展,不需要再去修改已有的规则。还可以应用于所有的表单校验。
总结: 在程序中使用策略模式就需要策略类,写策略类就需要先知道所有的策略。这比把所有逻辑多起在一起使用if-else要好得多。