JavaScript 策略模式
前言
策略模式常常用于解耦代码中的多种方案实现,比如表单校验,我们将校验的规则解耦出来,封装成一个个策略,然后在需要使用的dom上,使用这些策略,从而省去在具体的校验函数中一大堆if else的判断,当然也许不是if else。
再举个例子,比如代码的压缩方式,我们可以使用gzip,或者br的方式,或者其他,他们都是为了达到压缩目的,又是多种方案可选的形式。
使用策略模式计算奖金
很多公司的年终奖是根据员工的工资基数和年底绩效情况来发放的。例如,绩效为 S 的人年终奖有 4 倍工资,绩效为 A 的人年终奖有 3 倍工资,而绩效为 B 的人年终奖是 2 倍工资。假设财务部要求我们提供一段代码,来方便他们计算员工的年终奖
var calculateBonus = function(performanceLevel, salary) {
if (performanceLevel === 'S') {
return salary * 4;
}
if (performanceLevel === 'A') {
return salary * 3;
}
if (performanceLevel === 'B') {
return salary * 2;
}
};
calculateBonus('B', 20000); // 输出:40000
calculateBonus('S', 6000); // 输出:24000
可以看到这种方式导致在计算函数中存在大量的if else,我们可以优化一下,使用组合的方式,将计算的逻辑抽离出去。
var performanceS = function(salary) {
return salary * 4;
};
var performanceA = function(salary) {
return salary * 3;
};
var performanceB = function(salary) {
return salary * 2;
};
var calculateBonus = function(performanceLevel, salary) {
if (performanceLevel === 'S') {
return performanceS(salary);
}
if (performanceLevel === 'A') {
return performanceA(salary);
}
if (performanceLevel === 'B') {
return performanceB(salary);
}
};
calculateBonus('A', 10000); // 输出:30000
但是这种优化非常有限,calculateBonus 会因为计算函数的增多,变的越来越大。
模仿强类型语言的策略模式
为此我们可以使用策略模式来改善上述代码,但是为了我们更好的理解js的写法,我们先去模仿强类型语言中的写法。
var performanceS = function() {};
performanceS.prototype.calculate = function(salary) {
return salary * 4;
};
var performanceA = function() {};
performanceA.prototype.calculate = function(salary) {
return salary * 3;
};
var performanceB = function() {};
performanceB.prototype.calculate = function(salary) {
return salary * 2;
};
// 接下来定义奖金类 Bonus:
var Bonus = function() {
this.salary = null; // 原始工资
this.strategy = null; // 绩效等级对应的策略对象
};
Bonus.prototype.setSalary = function(salary) {
this.salary = salary; // 设置员工的原始工资
};
Bonus.prototype.setStrategy = function(strategy) {
this.strategy = strategy; // 设置员工绩效等级对应的策略对象
};
Bonus.prototype.getBonus = function() { // 取得奖金数额
return this.strategy.calculate(this.salary); // 把计算奖金的操作委托给对应的策略对象
};
在强类型语言中,他们的函数不是一等公民,所以策略的传递都是使用的类,而我们这使用构造函数模拟类。
在多态的文章中有提到,为了实现多态,强类型语言会向上转型,所以在具体的强类型实现中,他们会声明一个抽象类或者接口,用于在setSalary
中声明策略类型,但是js中不用,这里就省略了。
你会发现整个代码是有些繁琐的,实际上在js中我们根本没必要搞的这么麻烦。
js的策略模式实现
var strategies = {
"S": function(salary) {
return salary * 4;
},
"A": function(salary) {
return salary * 3;
},
"B": function(salary) {
return salary * 2;
}
};
var calculateBonus = function(level, salary) {
return strategies[level](salary);
};
console.log(calculateBonus('S', 20000)); // 输出:80000
console.log(calculateBonus('A', 10000)); // 输出:30000
可以看到,我们完全可以将计算的逻辑封装成函数,然后函数统一存放在strategies
对象中,计算的时候只需要传入对应的key就能拿到对应的策略。
这种封装方式也可以用在表单的校验上,比如在Element UI的表单校验中,就是使用了这种套路。
函数与策略模式
因为强类型语言的限制性,导致他们只能使用class类来实现,而我们JavaScript本身语言就很灵活,函数作为一等公民,在无形之中就承载了这种策略模式的特性。我们可以将函数作为参数、结果、赋值变量、包含在数据结构中,这种特性使得我们在无形之中就忽略了这种意义,就像很普通的在使用一样,完全想不到这方面去。
我们常常会封装函数,在函数中写入不同的代码逻辑,从而在调用的时候返回不同的结果,函数的这种“多态性”使得它成为了一个个策略,尽管这种策略在平时都是“隐形”的。
所以至此,你应该也会明白,为什么js中的策略模式,大部分人的教程都是这种形式了,一个对象里面不同的key存储不同的函数,然后在某一个函数里统一调用。
本文系作者 @木灵鱼儿 原创发布在木灵鱼儿站点。未经许可,禁止转载。
暂无评论数据