看本文之前需要有JavaScript的基础知识


策略模式的定义是:定义一系列算法,把它们一个个封装起来,并且使它们可以相互替换。

一、使用策略模式计算奖金

  1. 最初代码实现:

    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',20000); // 40000
    calculateBonus('S',6000); // 24000

    这段代码十分简单,但是存在着显而易见的缺点:

    • calculateBonus函数比较庞大,包含了很多if-else语句,这些语句需要覆盖所有的逻辑分支。

    • calculateBonus函数缺乏弹性违反开放-封闭原则

    • 算法的复用性差

  2. 使用组合函数重构代码

    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('A',10000); // 30000
  3. 使用策略模式重构代码

    策略模式指的是定义一系列的算法,把它们一个个封装起来。将不变的部分和变化的部分隔开是每个设计模式的主题,策略模式的目的就是将算法的使用与算法的实现分离开来。

    一个基于策略模式的程序至少由两部分组成。第一个部分是一组策略类,策略类封装了具体的算法,并负责具体的计算过程。第二个部分是环境类Context,Context接受客户的请求,随后把请求委托给某一个策略类。

    也可以有三种:抽象策略(strategy)类、具体策略(Concrete Strategy)类、环境(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(strategy){
    this.strategy = strategy; // 设置员工绩效等级对应的策略对象
    };

    Bonus.prorotype.getBonus = function(){ // 取得奖金数额
    if(!this.strategy){
    throw new Error('未设置strategy属性');
    }
    return this.strategy.calculate(this.salary); // 把计算奖金操作委托给对应的策略对象
    };

    var bonus = new Bonus();

    bonus.setSalary(10000);
    bonus.setStrategy(new performanceS()); // 设置策略对象

    console.log(bonus.getBonus()); // 40000

    bonus.setStrategy(new performanceA()); // 设置策略对象
    console.log(bonus.getBonus()); // 30000

二、JavaScript版本的策略模式

实际上在JavaScript语言中,函数也是对象,所以更简单和直接的做法是把strategy直接定义为函数:

将判断语句改写成对象形式可以消除大片的条件分支语句

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',20000)); // 80000
console.log(calculateBonus('A',10000)); // 30000

三、多态在策略模式中的体现

通过使用策略模式重构代码,我们消除了原程序中大片的条件分支语句。所有跟计算奖金有关的逻辑不再放在Context中,而是分布在各个策略对象中。Context并没有计算奖金的能力,而是把这个职责委托给了某个策略对象。每个策略对象负责的算法已被各自封装在对象内部。当我们对这些策略对象发出”计算奖金”的请求时,它们会返回各自不同的计算结果,这正是对象多态性的体现,也是”它们可以相互替换”的目的。替换Context中当前保存的策略对象,便能执行不同的算法来得到我们想要的结果。

四、策略模式的优缺点

  1. 优点:

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

    • 使用策略模式会在程序中增加许多策略类或者策略对象,但实际上这比把它们负责的逻辑堆砌在Context中要好。

    • 要使用策略模式,必须了解所有的strategy,必须了解各个strategy之间的不同点,这样才能选择一个合适的strategy。

五、小结

在JavaScript语言的策略模式中,策略类往往被函数所代替,这时策略模式就成为一种”隐式”的模式。