策略模式
定义:定义一系列的算法,把它们一个一个封装起来,并且使它们可以相互替换。
使用策略模式计算奖金
需求为绩效S的人4倍奖金,绩效为A的人3倍奖金,绩效为B的人年终奖是2倍工资。
var caculateBonus = function(performanceLevel, salary){
if(performanceLevel === 'S'){
return salary * 4;
}
if(performanceLevel === 'A'){
return salary * 3;
}
if(performanceLevel === 'B'){
return salary * 2;
}
}
这段代码有一些缺点,如果我们想改变绩效的奖金系数,我们必须修改calculateBonus函数的内部实现,这是违反开放-封闭原则的。算法的复用性很差,如果在程序的其他地方需要重用这些计算奖金的方法,就只有复制和粘贴。
使用组合函数重构代码
我们把各种算法封装到一个个小函数里面,这些小函数有着良好的命名,可以一目了然知道它对应者哪种算法
var performanceS = function(salary){
return salary * 4;
};
var performanceA = function(salary){
return salary * 3;
};
var performanceB = function(salary){
return salary * 2;
}
var calculateBonus = function(performanceLevel, salary){
if(performanceLevel === 'S'){
return performanceS(salary);
}
//...
}
这样依然没有解决我们的问题\
使用策略模式重构代码
一个基于策略模式的程序至少有两部分组成,第一个部分是策略类,策略类封装了具体的算法,并负责具体的计算过程。第二个部分是环境类Context,Context接受客户的请求,随后把请求委托给一个策略类。要做到这点,说明Context中要维持某个策略对象的引用
var performanceS = function(){};
performanceS.prototype.calculate = function(salary){
return salary * 4;
};
var performanceA = function(){};
performanceA.prototype.calculate = function(salary){
return salary * 3;
};
var performanceB = function(){};
performanceB.prototype.calculate = function(salary){
return salary * 2;
};
接下来定义奖金类Bonus
var Bonus = function(){
this.salary = null;
this.strategy = null; //绩效等级对应的策略对象
}
Bonus.prototype.setSalary = function(salary){
this.salary = salary;
}
Bonus.prototype.setStrategy = function(salary){
this.strategy = strategy;
}
Bonus.prototype.getBonus = function(){
return this.strategy.calculate(this.salary);
}
接下来
var bonus = new Bonus();
bonus.setSalary(10000);
bonus.setStrategy(new performanceS());//设置策略对象
console.log(bonus.getBonus());
bonus.setStrategy();
js版本的策略模式
我们让strategy对象从各个策略类中创建而来,这时模拟一些传统的面向对象语言的实现,实际上在js中,函数也是对象,所以更简单的做法是把strategy直接定义成函数
//[ˈstrætədʒi]策略
var strategies = function(){
'S': function(salary){
return salary * 4;
},
'A': function(salary){
return salary * 3;
},
'B': function(salary){
return salary * 2;
}
};
同样,Context也没有必要必须用Bonus类表示,我们依然用calculateBonus函数充当Context来接受用户的请求
var calculateBonus = function(level, salary){
return strategies[level](salary);
}
console.log(calculateBonus('S', 20000));
console.log(calculateBonus('A', 10000));
表单校验
我们首先要将表单的校验逻辑都封装成策略对象
var strategies = {
isNonEmpty: function(value, errorMsg){
if(value === ''){
return errorMsg;
}
},
minLength: function(value, length, errorMsg){
if(value.length < length){
return errorMsg;
}
},
isMobile: function(value){
if(!/(^1[3|5|8][0-9]{9}$)/.test(value)){
return errorMsg;
}
}
}
然后实现一个validataFunc类
var Validator = function(){
//保存校验规则
this.cache = [];
};
Validator.prototype.add = function(dom, rule, errorMsg){
//把strategy和参数分开
var arr = rule.split(':');
this.cache.push(function(){
//statefy: minLength
var strategy = arr.shift();
//[dom.value, 10]
arr.unshift(dom.value);
//[dom.value, 10, errorMsg]
arr.push(errorMsg);
//return minLength.apply(dom, [dom.value, 10, errorMsg])
return strategies[strategy].apply(dom, arr);
})
};
Validator.prototype.start = function(){
for(var i = 0; i < this.cache.length; i++;){
var msg = this.cache[i];
//如果有确切的返回值,说明校验没有通过
if(msg){
return msg;
}
}
}
这样重构之后,我们在修改某个校验规则的时候,只需要编写或者改写少量的代码
validator.add(registerForm.userName, 'minLength: 10', '用户名长度不能小于10位');
策略模式也有一些缺点,使用策略模式会在程序中增加许多策略对象