前言

策略模式常常用于解耦代码中的多种方案实现,比如表单校验,我们将校验的规则解耦出来,封装成一个个策略,然后在需要使用的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存储不同的函数,然后在某一个函数里统一调用。

分类: JavaScript设计模式与开发实践 标签: JavaScript模式一等公民策略模式高阶函数

评论

暂无评论数据

暂无评论数据

目录