第五章、策略模式

定义:定义一些列算法,把他们一一封装,并且他们可以相互替换。

核心:将算法的实现和算法的使用分离

一个基于策略模式的程序至少由两部分组成。第一个部分是一组策略类,第二个部分是环境类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))//输出:80000console.log(calculateBonus('A',10000))//输出:30000

策略模式的优缺点

  • 策略模式利用组合、委托、和多态等技术和思想,可以有效地避免多重条件选择语句。
  • 策略模式提供了对开放-封闭原则的完美支持,将算法封装在独立的strategy中,使得他们易于切换,易于理解易于扩展
  • 策略模式中的算法也可以复用在系统的其他地方,复用性高。
  • 在策略模式中利用组合和委托来让Context拥有执行算法的能力,也是继承的一种更轻便的替代方案

缺点:

  • 使用策略模式会在程序中增加许多策略类和策略对象,但实际上这比把它们负责的逻辑堆砌在Context中要好
  • 另外,还需要对strategy熟悉,比如旅游规划,出行方式的选择:飞机、火车、驾车等细节。