第五章、策略模式
定义:定义一些列算法,把他们一一封装,并且他们可以相互替换。
核心:将算法的实现和算法的使用分离
一个基于策略模式的程序至少由两部分组成。第一个部分是一组策略类,第二个部分是环境类Context。
- 策略类:策略类封装了具体的算法,并负责具体的计算过程。
- 环境类Context:环境类Context接受客户的请求,随后把请求委托给某一个策略类。
案例:计算奖金,绩效分别为S、A、B,奖金分别为4、3、 2倍;
最初代码实现
var calculateBunus = function(preformancelevel, salary) {
if(preformancelevel === 'S') {
return salary * 4;
}
if(preformancelevel === 'A') {
return salary * 3;
}
if(preformancelevel === 'B') {
return salary * 2;
}
}
calculateBunus('B', 2000) // 4000;
calculateBunus('S', 4000) // 16000;
- 代码简单明了,但缺点也显而易见:
calculateBunus函数比较庞大,包含了很多if-else语句,这些语句需要覆盖所有逻辑的分支。 - calculateBunus函数缺乏弹性,如果新增一个“C”字段,或者把"S"的奖金系数改为5倍,必须在calculateBunus函数内部修改,这样就违反了开放-封闭的原则。
- 算法的复用性差,如果程序中其他地方需要重用这些计算奖金的算法呢?只能CTRL+CV;
利用策略模式重构代码:
我们把所有的绩效以及奖金分别写成单独的函数
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(strategy) {
this.strategy = strategy; //设置员工绩效等级对应的策略对象
}
Bonus.prototype.getBonus = function() { //取得奖金数额
if(!this.strategy) {
throw new Error('未设置‘strategy属性');
}
return this.strategy.calculate(this.salary); //把计算奖金的操作委托给对应的策略对象
}
调用:
var bonus = new Bonus();
bonus.setSalary(1000);
bonus.setStrategy(new performanceS()); //设置策略对象
console.log(bonus.getBonus()) //输出:4000
Javascript版的策略模式
上面所说的是让strategy对象从各个策略类中创建而来,在js中,可以把strategy直接定义成函数:
var strategies = {
"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)) //输出:80000
console.log(calculateBonus('A',10000)) //输出:30000
策略模式的优缺点
- 策略模式利用组合、委托、和多态等技术和思想,可以有效地避免多重条件选择语句。
- 策略模式提供了对开放-封闭原则的完美支持,将算法封装在独立的strategy中,使得他们易于切换,易于理解易于扩展
- 策略模式中的算法也可以复用在系统的其他地方,复用性高。
- 在策略模式中利用组合和委托来让Context拥有执行算法的能力,也是继承的一种更轻便的替代方案
缺点:
- 使用策略模式会在程序中增加许多策略类和策略对象,但实际上这比把它们负责的逻辑堆砌在Context中要好
- 另外,还需要对strategy熟悉,比如旅游规划,出行方式的选择:飞机、火车、驾车等细节。