学习JavaScript 设计模式 - 策略模式

387

前面两章讲的都是常见的 创建型设计模式, 这章讲一个 行为型设计模式

定义

定义一系列的算法, 把它们封装起来, 并且使它们可以互相替换

意图

解决在很多相似场景下用过多 if else 所带来的复杂和难以维护问题

例子: 公司绩效考核与年终奖

    class Kpi {
        constructor(grade, base) { //1.等级   2.奖金基数
            this.grade = grade 
            this.base = base
        }
        
        //奖金
        bonus() {
            if (this.grade === 'D') {
                return '恭喜恭喜! 喜提 n+1'  
            }
            
            if (this.grade === 'C') {
                return this.base * 2  //双倍工资
            }
            
             if (this.grade === 'B') {
                return this.base * 4  //4倍工资
            }
            
             if (this.grade === 'A') {
                return this.base * 6  //6倍工资
            } 
        }
    } 
 

这样公司财务计算每个人的年底奖金就非常方便了,

比如小王 评级B 底薪10000

const wang = new Kpi('B', 10000)
wang.bounds()  // 40000 

上面这个就是没用策略模式的写法, 功能没啥问题, 就是看着像裹脚布又臭又长, 每次新增都得往里面塞, 这个方法会越来越长。

这还只是一个简单例子, 要是判断键盘按键 那不得判断108键啊!!!!

接下来我们用策略模式改造下

    class Kpi {
        static grade
        constructor(grade, base) { //1.等级   2.奖金基数
            this.grade = grade 
            this.base = base
        }
        
        bonus() {
            return {
                A: this.isA,
                B: this.isB,
                C: this.isC,
                D: this.isD,
            } [this.grade]
        },
        
        isA() {
            return this.base * 6  
        }
        isB() {...}
        isC() {...}
        isD() {...}
    }

感觉结构清晰多了, 后续如果有其它等级加进来也很方便, 不会把bonus 方法搞炸了。

类似的案例还有前端表单验证

class Rule {
    //验证身份证
    isCardNumber(value) {
        const rel = /^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$/
        
        return rel.test(value)
    }
    isMobile(value) {
        ...
    }
    
    // 如果想提供报错信息,可以这么写
    async isEmpty(value, err) {
        if( value.length< 1) {
            alert(err)
            return false
        }
    }
}

项目中我们可以根据需求往里面添加方法, 至于想要 链式调用 或者 同步 的写法都可以, 比如要同步那么就可以如下写法

    //这里举个栗子
    isEmpty(value) {
        return new Promise((resolve, reject) => {
            if(value.length < 1) {
                reject('该选项不能为空')
            }
            resolve(true)
        })
    }
    async function submit() {
        const myRule = new Rule()
        await myRule.isEmpty(this.name, '名字不能为空哦')
        await myRule.isMobile(this.phone)
    }
  

最后的轻语

永远不要放弃寻找第三选择

我们写代码也是, 时刻想着是否有更好的方案, 慢慢的就能写出可维护的代码了