策略模式的定义是:定义一系列的算法,把它们都封装起来,并使它们可以互相替换。
实例一:使用策略模式计算奖金
很多公司的年终奖是根据员工的工资基数和年底绩效情况来发放的。例如,绩效为S的人年终奖有4倍工资,绩效为A的人年终奖为3倍工资,绩效为B的人年终奖为2倍工资。代码的最初实现为:
var calculateBonus = function(performanceLevel, salary){
if(performanceLevel == 'S'){
return salary * 4;
}
if(performanceLevel == 'A'){
return salary * 3;
}
if(performanceLevel == 'B'){
return salary * 2;
}
}
calculateBonus('B',2000); // 输出:4000
calculateBonus('s',5000); // 输出:20000
上面的代码非常简单,但有很明显的缺点。
1.calculateBonus函数也许会非常庞大,那会包含很多if-else语句
2.calculateBonus函数缺乏弹性,如果增加一种新的绩效,或者想修改绩效,那就必须得深入calculateBonus函数内部实现,这是违反开放-封闭原则的。
3.算法的复用性差,如果要在程序的其它地方重用这些代码,就只能复制和粘贴。
所以这段代码需要重构。
使用组合函数重构代码:
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);
}
if(performanceLevel == 'A'){
return performanceA(salary);
}
if(performanceLevel == 'B'){
return performanceB(salary);
}
}
calculateBonus('s',5000); // 输出:20000
使用策略模式重构代码:
一个基于策略模式的程序至少由两部分组成。第一部分是一组策略类,封装了具体的算法,负责具体的计算过程。第二部分是环境类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;
}
var Bonus = function(){
this.salary = null; //原始工资
this.strategy = null; //绩效等级对应的策略对象
}
Bonus.prototype.setSalary = function(salary){
this.salary = salary; //设置员工原始工资
}
Bonus.prototype.setStrategy = function(stratege){
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()); // 输出 40000
javascript版本的策略模式:
var strategies = {
"S":function(salary){
return salary * 4;
},
"A":function(salary){
return salary * 3;
},
"B":function(salary){
return salary * 2;
}
};
var calculateBonus = function(level, salary){
return strategies[ level ]( salary );
};
console.log( calculateBonus( 'S' , 15000 ) ); // 输出 60000
策略模式的优缺点:
1.策略模式利用组合、委托和多态等技术思想,可以有效地避免多重条件选择语句
2.策略模式提供了对开放-封闭原则的支持,将算法封装在独立的对象中,这样易于切换、易于理解、易于理解
3.在策略模式中,可以将算法复用在程序的其它地方,避免复制粘贴代码
4.在粗略模式中利用组合和委托来让Context拥有执行算法的能力,这也是继承的一种更轻便的替代方案
缺点:
首先,使用策略模式会在程序中增加许多策略类或者策略对象(但实际上这比把它们负责的逻辑放在Context中要好)。
其次,要使用策略模式,就得了解策略模式中所有的选项,此时就必须要向客户暴露策略类的所有实现,这就违反了最少知识原则