简化分支判断的设计模式

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

时间: 2024-10-11 07:17:37

简化分支判断的设计模式的相关文章

DEDE模板中如何使用if进行多分支判断

我们在用dede做模板的时候,经常会遇到需要根据某个条件判断来分别显示不同的内容的情况,也就是dede模板中的多分支判断.比如典型的,我们需要根据风险管理数据库表中的风险等级字段risklevel的取值的不同分别显示不同的风险提示图标.如图1 所示. 红色框中的投资项目名称前面的图标表示了不同的风险等级,一共有AA,D,E,HR 4个风险等级.在数据库表中保存的分别是这4个等级取值中的一个,如图2 所示. 我们的风险等级字段risklevel 是可以在列表模板中获取到其取值的,因为我们在内容模型

javaScript之分支判断与内置对象

一,分支结构 单一选择结构(if) 二路选择结构(if/else) 内联三元运算符 ?: 多路选择结构(switch) var condition = true; if (condition) { alert("我将出现!"); } condition = false; if (condition) { alert("我不会出现!"); } else { alert("我会出现!"); } condition ="some string

C# 使用运算符重载 简化结果判断

执行某个方法后, 一般都要对执行结果判断, 如果执行不成功, 还需要显示错误信息, 我先后使用了下面几种方式 /// <summary> /// 返回int类型结果, msg输出错误信息 /// </summary> /// <param name="param">输入参数</param> /// <param name="msg">错误信息</param> /// <returns>

if分支判断

# 控制语句 分支 循环语句 # 判断语句 if ..elif..else # if 条件语句(比较 逻辑 成员运算) # 空数据 == False # 非空数据 == True age = 20 if age > 18: print("恭喜你,成年了") s = "hello" if "o" in s: print("在的") s = "2" if s: print("ok")

MySQL case when条件分支判断

举例说明 SELECT CASE a.AFTER_QUALITY WHEN '0' THEN '过免维期' WHEN '1' THEN '未过免维期' ELSE '错误数据' END AS AFTER_QUALITY FROM t_maintain_log a 数据库表内容: 运行结果:

oracle 定义临时变量,并使用分支判断

declare tempCount int; tempID VARCHAR2(8); begin select count(*) into tempCount from CUSTOMER_PROFILE where id=:CUSTOMER_ID; if tempCount>0 then update CUSTOMER_PROFILE set name=:CUSTOMER_NAME,address=:CUSTOMER_ADDRESS,phone=:CUSTOMER_PHONE,descripti

if-else优化

过多if-else分支的优化 超过3个就应该去优化,说if-else过多的分支可以使用switch或者责任链模式等等方式来优化.确实,这是一个小问题,不过我们还是可以整理一下这个小问题的重构方式. 为什么要优化? 你没有看错.这是要放在第一条谈论的. 有许多人会说,叠起来一堆if-else分支,代码就不优雅了.可是,怎样去定义“优雅”的概念呢?再退一步说,即便不“优雅”,又有什么问题? 对于这样一段再普通不过的代码: 1 2 3 4 5 6 7 8 int code; if("Name"

列表生成式&amp;三元表达式

列表生成式: 用于生成一个新的列表,是Python内置的一种极其强大的生成list的表达式.一.语法格式:[exp for iter_var in iterable] 工作过程: (1)迭代iterable中的每个元素: (2)每次迭代都先把结果赋值给iter_var,然后通过exp得到一个新的计算值: (3)最后把所有通过exp得到的计算值以一个新列表的形式返回. 例如: L=[i for i in range(10)] print(L)#[0, 1, 2, 3, 4, 5, 6, 7, 8,

5.1.24 Python之列表生成式、生成器、可迭代对象与迭代器

语法糖的概念 列表生成式 生成器(Generator) 可迭代对象(Iterable) 迭代器(Iterator) Iterable.Iterator与Generator之间的关系 一.语法糖的概念 "语法糖",从字面上看应该是一种语法."糖",可以理解为简单.简洁.其实我们也已经意识到,没有这些被称为"语法糖"的语法,我们也能实现相应的功能,而 "语法糖"使我们可以更加简洁.快速的实现这些功能. 只是Python解释器会把这