先来举个例子,比如我们要去某个地方旅游,可以根据具体的实际情况来选择出行的线路。
- 坐飞机。
- 坐大巴或者火车。
- 骑自行车。
这些方法(算法)灵活多样,而且可以因为实际情况随意互相替换。这也是接下来要讲的策略模式的思想。
策略模式是开发中常用的第二种设计模式,它在开发中非常常见,由两部分组成。第一部分是策略类,封装了许多具体的,相似的算法。第二部分是环境类,接受客户请求,随后将请求委托给策略类。说的通俗一点就是将相同算法的函数存放在一个包装里边,每个函数用相同的方式拿出来,就叫做策略模式。下面我们来通过代码实现深入了解一下。
使用策略模式计算奖金
假设公司的年终奖是根据员工的工资基数和年底绩效情况来发放的。例如,绩效为C的人年终奖有4倍工资,绩效为A的人年终奖有3倍工资,而绩效为B的人年终奖是2倍工资。假设财务部要求我们提供一段代码,来方便他们计算员工的年终奖。
最开始的代码
我们可以编写一个名为calculateBonus的函数来计算每个人的奖金数额。很显然,calculateBonus函数要正确工作,就需要接收两个参数:员工的工资数额和他的绩效考核等级。
// 计算奖金 最开始想到的办法
let calculateBonus = function (performanceLevel, salary) {
if (performanceLevel === "A") {
return salary * 3;
}else if(performanceLevel === "B") {
return salary * 2;
}else if (performanceLevel === "C") {
return salary * 4;
}
};
calculateBonus('B', 3000); //输出 6000
calculateBonus('C', 2000); //输出 8000
这样做的缺点很明显:
- 函数逻辑庞杂,包含了大量的if-else语句
- 可扩展性差,如果发生改变,需要深入函数内部改动,违反开闭原则
- 复用性差
使用组合函数重构代码
把各种算法封装到一个个的小函数里面,根据命名,可以一目了然地知道它对应着哪种算法,它们也可以被复用在程序的其他地方。
const performanceC = function (salary) {
return salary * 4
}
const performanceA = function (salary) {
return salary * 3
}
const performanceB = function (salary) {
return salary * 2
}
let calculateBonus = function (performanceLevel, salary) {
if (performanceLevel === "A") {
return performanceA(salary);
}else if (performanceLevel === "B") {
return performanceB(salary);
}else if (performanceLevel === "C") {
return performanceC(salary);
}
};
calculateBonus('B', 3000); //输出 6000
calculateBonus('C', 2000); //输出 8000
好像好点了,但是依然很复杂,可扩展性依旧很差
使用策略模式重构代码
两种方式:
传统的面向对象方式
let performanceC = function () { };
performanceC.prototype.calculate = function () {
return salary * 4;
}
let performanceA = function () { };
performanceA.prototype.calculate = function () {
return salary * 3;
}
let performanceB = function () { };
performanceB.prototype.calculate = function () {
return salary * 2;
}
// 接下来定义奖金类
let Bonus = function () {
this.salary = null; //原始工资
this.strategy = null; //绩效等级对应的策略对象
}
Bonus.prototype.setSalary = function (salary) {
this.salary = salary; //设置员工的原始工资
}
Bonus.prototype.setStrategy = function (salary) {
this.strategy = strategy; //设置员工的绩效等级对应的策略对象
}
Bonus.prototype.getBonus = function () {
//取得的奖金数额
return this.strategy.calculate(this.salary); //把计算奖金的操作委托给
//对应的策略对象等级对应的策略对象
}
先创建一个bonus对象,并且给bonus对象设置一些原始的数据,比如员工的原始工资数额。接下来把某个计算奖金的策略对象也传入bonus对象内部保存起来。当调用bonus.getBonus()来计算奖金的时候,bonus对象本身并没有能力进行计算,而是把请求委托给了之前保存好的策略对象
bonus.setSalary(10000);
bonus.setStrategy(new performanceC()); //设置策略对象
console.log(bonus.getBonus()); //输出40000
bonus.setStrategy(new performanceA()); //设置策略对象
console.log(bonus.getBonus()); //输出30000
js策略模式 我们让strategy对象从各个策略类中创建而来,这是模拟一些传统面向对象语言的实现。实际上在JavaScript语言中,函数也是对象,所以更简单和直接的做法是把strategy直接定义为函数
/*策略类*/
var strategies = {
"C": function (salary) {
return salary * 4;
},
"A": function (salary) {
return salary * 3;
},
"B": function (salary) {
return salary * 2;
},
同样,Context也没有必要必须用Bonus类来表示,我们依然用calculateBonus函数充当Context来接受用户的请求。经过改造,代码的结构变得更加简洁:
/*环境类*/
var calculateBonus = function (level, salary) {
return strategies[level](salary);
}
console.log(calculateBonus('C', 20000)); //输出80000
console.log(calculateBonus('A', 10000)); //输出30000