zoukankan      html  css  js  c++  java
  • 简化分支判断的设计模式

      很多时候会发现自己在写代码的时候写了一坨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 = 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设计模式》

  • 相关阅读:
    HTTP下载文件校验失败原因分析与解决
    读《软件测试的艺术》
    CXF wsdl2java 错误
    oracle 存储过程 多参数 多返回值
    ORACLE 函数 调用
    typescript学习入门(学习笔记)
    js常用方法总结
    jenkins安装及项目构建发布回滚
    Centos8中创建LVM精简逻辑卷
    k8s kubectl命令自动补全
  • 原文地址:https://www.cnblogs.com/WeiRuifeng/p/7265972.html
Copyright © 2011-2022 走看看