再通过一个 Context class 来提供统一的对外接口,Context 内部再调用不同的 策略 方法
一个简单例子
假设要写一个可以同时处理两数加减乘除的函数,我们可能会实现成以下的样子:
doMath 函数接收两个操作数 a 和 b,以及一个算术类型 operation 作为参数,然后根据不同的算术类型返回不同的数学计算结果。
constdoMath= (a, b, operation) => {switch (operation) {case'ADD':return a + bcase'MINUS':return a - bcase'MULTIPLY':return a * bcase'DIVIDE':return a / bdefault:return }}doMath(1,2,'ADD') // 3doMath(1,2,'MINUS') // -1doMath(1,2,'MULTIPLY') // 2doMath(1,2,'DIVIDE') // 0.5
可以观察到,多次调用 doMath 函数的共同点在于:
都是输入两个数字
得到这两个数字经过某种计算后的结果作为返回值
而不同点就在于:
具体的计算方式是不一样的
这个问题模式就很适合使用策略模式来解决。
套用策略模式
用 OOP 的形式来实现的话,我们先把上面的例子改写成 class 的形式吧,改写方式之一:
classSimpleMath {constructor() {this.operations = { ['ADD']: (a, b) => a + b, ['MINUS']: (a, b) => a - b, ['MULTIPLY']: (a, b) => a * b, ['DIVIDE']: (a, b) => a / b } }calculate(a, b, operation) {returnthis.operations[operation](a, b) }}
// 不同算法被抽离成不同的 class// 实现的效果是这些 class 的都应该有相同的实例方法,但这些实例方法做的事情不一样// p.s. 按理说这些 class 都应该实现同样的 interface,但 JS 中并没有 interfaceclassAdd {calculate(a, b) {return a + b }}classMinus {calculate(a, b) {return a - b }}classMultiply {calculate(a, b) {return a * b }}classDivide {calculate(a, b) {return a / b }}// 原本的 SimpleMath 就只保留一个对具体算法实例的引用 operation// SimpleMath 并不关心具体使用什么算法,调用方在调用 SimpleMath 的时候把具体的算法对象传过来,SimpleMath 负责调用这个算法对象的 calculate 方法classSimpleMath {constructor(operation) {// operation 保存着具体算法实例this.operation = operation }calculate(a, b) {// SimpleMath 的 calculate 方法只是负责调用具体算法实例的 calculate 方法returnthis.operation.calculate(a, b) }}// 调用方代码:// 调用方必须知道自己需要的是哪种算法,并把对应的算法实例传给 SimpleMathconstadd=newSimpleMath(newAdd())add.calculate(1,2) // 3constmultiply=newSimpleMath(newMultiply())multiply(1,2) // 2