策略模式

104 阅读4分钟

一、介绍

定义:

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

个人理解:

将算法的使用和算法的实现分离。更大白话一些是指把if else封装成函数执行者,把if else中的不同情况下的处理封装成单独的处理函数,然后在函数执行者中调用处理函数。

主要解决:

在有多种算法相似的情况下,使用 if...else 所带来的复杂和难以维护。

如何解决:

将这些算法封装成一个一个的类,任意地替换。(Java)

将这些算法封装成一个一个的函数,任意地替换。(JavaScript

关键代码:

实现同一个接口。

应用实例:

  1. 表单的输入规则的验证,vue中流行的表单验证库VeeValidate

使用场景:

  1. 一个业务中需要根据不同条件进行不同计算。

优点:

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

缺点:

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

  2. 要使用策略模式,必须了解所有的strategy,必须了解各个strategy之间的不同点,这样才能选择一个合适的strategy。比如,我们要选择一种合适的旅游出行路线,必须先了解选择飞机、火车、自行车等方案的细节。此时strategy要向客户暴露它的所有实现,这是违反最少知识原则的。(所有策略类都需要对外暴露)

二、代码示例

根据薪资计算奖金,输入薪资的数额和奖金的计算方法,得到奖金的数额

普通写法(if else

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

策略模式

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
简写
var S = function( salary ){
  return salary * 4;
};

var A = function( salary ){
  return salary * 3;
};

var B = function( salary ){
  return salary * 2;
};

var calculateBonus = function( func, salary ){
  return func( salary );
};

calculateBonus( S, 10000  );    // 输出:40000

四、基于传统面向对象语言的模仿

一个基于策略模式的程序至少由两部分组成。

第一个部分是一组策略类,策略类封装了具体的算法,并负责具体的计算过程。

第二个部分是环境类Context, Context接受客户的请求,随后把请求委托给某一个策略类。要做到这点,说明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 (strategy) {
    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

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