Javasript设计模式-策略模式

82 阅读4分钟

先来举个例子,比如我们要去某个地方旅游,可以根据具体的实际情况来选择出行的线路。

  • 坐飞机。
  • 坐大巴或者火车。
  • 骑自行车。

这些方法(算法)灵活多样,而且可以因为实际情况随意互相替换。这也是接下来要讲的策略模式的思想。

策略模式是开发中常用的第二种设计模式,它在开发中非常常见,由两部分组成。第一部分是策略类,封装了许多具体的,相似的算法。第二部分是环境类,接受客户请求,随后将请求委托给策略类。说的通俗一点就是将相同算法的函数存放在一个包装里边,每个函数用相同的方式拿出来,就叫做策略模式。下面我们来通过代码实现深入了解一下。

使用策略模式计算奖金

假设公司的年终奖是根据员工的工资基数和年底绩效情况来发放的。例如,绩效为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