首页 > 代码库 > 简化分支判断的设计模式

简化分支判断的设计模式

  很多时候会发现自己在写代码的时候写了一坨if else 语句使得自己的代码看起来很丑,随着业务量的增大,代码变得很难维护,之前想到能替换if else的只有switch,其实效果并没有明显的提升,现在在看设计模式方面的知识,发现两种设计模式能够解决分支判断的臃肿问题。

状态模式

使用场景

  大家都知道超级玛丽的游戏吧,玛丽要吃蘑菇,他就要挑起,顶出墙壁里的蘑菇;玛丽想到悬崖的另一边,他就要跳起;玛丽想躲避被前面的乌龟咬到,他就要开枪打死乌龟;前面飞过炮弹,玛丽就要蹲下躲避;时间不够了,就要加速奔跑···

  如果这个游戏要用if或者switch条件判断,显得有些疲惫,如果使用状态模式将‘跳跃’、‘开枪’、‘蹲下’和‘奔跑’作为一个个的状态来开发,之后在遇到不同情况的时候直接使用状态,思路将变得清晰。

代码实现

  首先创建一个状态对象,内部保存状态变量,然后内部封装好每种动作对应的状态,最后状态对象返回一个接口对象,它可以对内部的状态修改或者调用。

代码如下:

/** 超级玛丽里面的状态: 跳跃、开枪、蹲下、奔跑等 */const MarryState = function() {    // 内部状态私有变量    let _currentState = {};    // 动作与状态方法映射    const states = {        jump() {            // 跳跃            console.log(‘jump‘);        },        move() {            // 移动            console.log(‘move‘);        },        shoot() {            // 射击            console.log(‘shoot‘);        },        squat() {            // 蹲下            console.log(‘squat‘);        }    };    // 动作控制类    const Action = {        // 改变状态方法        changeState() {            // 组合动作通过传递多个参数实现            let arg = arguments;            // 重置内部状态            _currentState = {};            if(arg.length) {                // 遍历动作                for(let i = 0,len = arg.length; i < len; i++) {                    // 向内部状态中添加动作                    _currentState[arg[i]] = true;                }            }            // 返回动作控制类            return this;        },        // 执行动作        goes() {            console.log(‘触发一次动作‘);            // 遍历内部状态保存的动作            for(let i in _currentState) {                // 如果改动作存在则执行                states[i] && states[i]();            }            return this;        }    };    // 返回接口方法 change、goes    return {        change: Action.changeState,        goes: Action.goes    }};

 

使用方式:

// 创建一个超级玛丽const marry = new MarryState();marry    .change(‘jump‘,‘shoot‘)                 // 添加跳跃与射击动作    .goes()                                 // 执行动作    .goes()                                 // 执行动作    .change(‘shoot‘)                        // 添加射击动作    .goes();                                // 执行动作

 

  可以发现,状态模式中的状态可以连续使用。

原理和优点

  原理:将条件判断的不同结果转化为对象的内部状态,作为对象内部的私有变量。

  好处:当我们需要增加、修改、调用、删除某种状态方法时就会很容易,也方便了我们对状态对象中内部状态的管理。

  用途:状态模式是为了解决程序中臃肿的分支判断语句问题,将每个分支转化为一种状态独立出来,方便每种状态的管理又不至于每次执行时遍历所有分支。

  总之,状态模式的最终目的是简化分支判断流程。

策略模式

使用场景

  商品的促销活动。在圣诞节,一部分商品5折出售,一部分商品8折出售,一部分商品9折出售,等到元旦,普通用户满100减30,高级VIP用户满100减50···

  如果这种情况用if或switch来写将是一件很费时费力的事情。

  而且对于圣诞节或者元宵节,当天的一种商品只有一种促销策略,根本不用关心其他的促销状态。

代码实现

  首先要将这些促销算法封装在一个策略对象内,然后对每种商品的策略调用时,直接对策略对象中的算法调用即可,为方便我们的管理与使用,我们需要返回一个调用接口对象来实现对策略算法对调用。

代码如下:

/* * 价格策略对象 */const PriceStrategy = function () {    // 内部算法对象    const strategy = {        // 100 返 30        return30(price) {            // parseInt可通过~~、|等运算符替换,要注意此时price要在[-2147483648,2147483647]之间            // +price 转换为数字类型            return +price + parseInt(price / 100) * 30;        },        // 100 返 50        return50(price) {            return +price + parseInt(price / 100) * 50;        },        // 9 折        percent90(price) {            // JavaScript 在处理小数乘除法有bug,故运算前转化为整数            return price * 100 * 90 / 10000;        },        // 8 折        percent80(price) {            // JavaScript 在处理小数乘除法有bug,故运算前转化为整数            return price * 100 * 80 / 10000;        },        // 5 折        percent50(price) {            // JavaScript 在处理小数乘除法有bug,故运算前转化为整数            return price * 100 * 50 / 10000;        }    };    // 策略算法调用接口    return function (algorithm, price) {        // 如果算法存在,则调用算法,否则返回false        return stragtegy[algorithm] && stragtegy[algorithm](price);    }}();

 

使用方式:

const price = PriceStrategy(‘return50‘, ‘343.20‘);console.log(price);

表单验证中的策略模式

代码如下:

/** 表单正则验证侧罗对象 */const InputStrategy = function () {    const strategy = {        // 是否为空        notNull(value) {            return /\s+/.test(value)        },        // 是否是一个数字        number(value) {            return /^[0-9]+(\.[0-9]+)?$/.test(value);        },        // 是否为本地电话        phone(value) {            // 例:010-94837837  或  0310-8899766            return /^\d{3}\-\d{8}$|^\d{4}\-\d{7}$/.test(value);        }    };    return {        // 验证接口 type 算法 value 表单值        check(type, value) {            // 去除首尾空白符            value = http://www.mamicode.com/value.replace(/^/s+|/s+$/g, ‘‘);            return strategy[type] ? strategy[type](value) : ‘没有该类型等检测方法‘;        },        // 添加策略        addStrategy(type, fn) {            strategy[type] = fn;        }    }};

  可以发现,表单验证返回的接口中添加了一个添加策略接口。因为已有的策略即使再多,有时候也不能满足其他工程师的需求,这样就可以增强策略对象的拓展性。

// 拓展  可以延伸算法InputStrategy.addStrategy(‘nickname‘, function (value) {    return /^[a-zA-Z]\w{3,7}$/.test(value);});

 

原理和优点

  策略模式:将定义的一组算法封装起来,使其相互之间可以替换。

  策略模式不需要管理状态、状态间没有依赖关系、策略之间可以相互替换、在策略对象内部保存的是相互独立的一些算法。

  策略模式使得算法脱离与模块逻辑而独立管理,使我们可以专心研发算法,而不必受模块逻辑所约束。

其他

  设计模式并不是很高深不可理解的一门学问,只是根据经验把复杂的业务逻辑整理清楚,使之更容易操作化。

  根据不同的业务逻辑选择不同的设计模式有助于简化代码,也有助于代码的解耦,使得代码更加有效和可维护。

  参考书籍:《JavaScript设计模式》

 

简化分支判断的设计模式