# 策略模式

策略模式是将一系列算法(行为)封装起来,使它们可以相互替换。它有以下优点:

  • 利用组合、委托和多态等技术和思想,将算法的使用与算法的实现分离开来,有效地避免了多重条件选择语句。
  • 提供了对开放——封闭原则的完美支持,将算法封装在独立的 strategy 中,使得它们易于切换、理解、复用和扩展。
  • 利用组合和委托让 Context 拥有执行算法的能力。

# 传统的策略模式

传统面向对象言语中的策略模式至少由以下两部分组成:

  • 一组策略类,每个策略类封装了具体的算法,用于负责具体的计算过程。
  • 环境类 Context,用于接收用户请求,并把请求委托给某一个策略类。

这里以实现一个简单计算器为例子,模仿实现如下:

// 加法策略类
const Addition = function () {}
Addition.prototype.calculate = (a, b) => a + b

// 减法策略类
const Subtraction = function () {}
Subtraction.prototype.calculate = (a, b) => a - b

// 乘法策略类
const Multiplication = function () {}
Multiplication.prototype.calculate = (a, b) => a * b

// 除法策略类
const Division = function () {}
Division.prototype.calculate = (a, b) => a / b

// Context
const Calculator = function () {
  this.a = 0
  this.b = 0
  this.strategy = null // 策略对象
}

Calculator.prototype.setParams = function (a, b) {
  this.a = a
  this.b = b
}

Calculator.prototype.setStrategy = function (strategy) {
  this.strategy = strategy
}

Calculator.prototype.calculate = function () {
  if (!this.strategy) {
    throw new Error('have you set a strategy to apply?')
  }

  return this.strategy.calculate(this.a, this.b) // 将计算操作委托给对应的策略对象
}

const calculator = new Calculator()
calculator.setParams(7, 11)
calculator.setStrategy(new Multiplication())
console.log(calculator.calculate()) // 77

# JavaScript 中的策略模式

由于在 JavaScript 中函数也是对象,同时可以作为函数参数传递,所以更简单的做法是将这些 strategy 直接定义为函数,封装在一个对象中,同时也不再需要 Context 类,而是直接通过函数充当 context 接收用户请求,利用对象多态性委托具体 strategy 函数。

这里沿用简单计算器的例子:

// 包含一系列策略对象的对象
const strategies = {
  addition: (a, b) => a + b,
  subtraction: (a, b) => a - b,
  multiplication: (a, b) => a * b,
  division: (a, b) => a / b
}

// context 函数
const calculator = (strategy, a, b) => {
  return strategies[strategy](a, b)
}

console.log(calculator('multiplication', 7, 11)) // 77