策略模式的定义:定义一系列算法,把它们一个个封装起来,并且使它们可以互相替换。
策略模式的定义看起来可能比较抽奖,下面我们用一个例子来解释它。
计算奖金
小明公司的年终奖会根据员工的工资基础和年底绩效来进行发放,如:绩效为 S 的人年终奖为 4 倍工资,A 的人为 3 倍工资,B 的人为 2 倍工资,如果让我们写代码来实现,该怎么实现呢?
不使用策略实现
function calculateBonus(leave, salary) {
if (leave === 'S') {
return salary * 4;
} else if (leave === 'A') {
return salary * 3;
} else if (leave === 'B') {
return salary * 2;
}
}
calculateBonus('S', 1000); // 4000
calculateBonus('A', 1000); // 3000
上面这样写的代码是很简单的,但是它存在一些问题:
- 所有计算规则堆积到了一起,包含了很多
if else
语句。 - 如果我们想要新增其他规则的时候就需要深入
calculateBonus
函数,这违反了开发封闭原则。 - 代码复用性差,假如说其他地方遇到用到
S
和A
算法时我们需要赋值粘贴过去。
策略默认实现
一个基于策略模式的程序至少由两部分组成,第一部分是策略类,用于封装具体的算法,第二部分是环境类 Context,用于接收用户的请求然后把请求委托给某个策略类。
现在我们来实现一下:
// 策略类
function LeaveS() {}
LeaveS.prototype.calculate = (salary) => salary * 4;
function LeaveA() {}
LeaveA.prototype.calculate = (salary) => salary * 3;
function LeaveB() {}
LeaveB.prototype.calculate = (salary) => salary * 2;
// Context 环境类
function Bonus(leave, salary) {
this.leave = leave;
this.salary = salary;
}
Bonus.prototype.getBonus = function() {
return this.leave.calculate(this.salary)
};
// 用户请求
new Bonus(new LeaveS(), 3000).getBonus(); // 12000
new Bonus(new LeaveA(), 3000).getBonus(); // 9000
当后面想要新增一个计算规则时,我们只需要创建一个对应的策略类就可以了,然后再用户请求时给 Bouns
传递对应的策略对象即可(互相替换),而且每个类的职责更明显了,复用性也变强了。
JavaScript 中的策略模式
上面的代码是基于传统的面向对象语言来实现的,在 JavaScript 中函数也是对象,且没有类型之间的限制,所以实现起来非常简单,我们只需要将策略类改为函数,然后把它们组合起来就好了。
// 策略函数
const strategies = {
S: (salary) => salary * 4,
A: (salary) => salary * 3,
B: (salary) => salary * 2,
};
// Context
const getBouns = (leave, salary) => {
return strategies[leave](salary);
};
getBouns('S', 3000); // 12000
getBouns('A', 3000); // 9000
更广义的策略模式
先来看一下策略模式的定义:
定义一系列算法,把它们一个个封装起来,并且使它们可以互相替换。
从定义上来看策略模式是用来定义算法的,但如果只用策略模式来定义算法就会显得有点大材小用,在实际开发中我们也可以用用来封装一些 “业务逻辑”,将臃肿的 if else
代码拆分出来。
整理与 JavaScript 设计模式与开发实践。