重构:烧脑判断大扫除

5,926 阅读4分钟

编写业务代码的时候常常会遇到判断,面对各种业务场景,你知道最优方案吗?

让我们看看基本情况:

初级:if else else if一把梭干就完事

中级:switch case、if else、多态、用设计模式包装

高深:清晰所有判断条件的边界,用解释性强且合理的方式


给各位大佬泡上一杯82年的卡布奇诺,细细品味。

代码初窥

需求:判断今日上班情况,当前时间和工作进度作为条件

    {
        period:afteroon,// 当前时间:afternoon || night
        workProgress:start,  // 工作进度: start || end
    }

if-else

    if(period === 'afternoon'){
        上班
    }else if(period === 'night' && workProgress !== 'end'){
        加班
    }else {
        下班
    }

优点: if else 最为常见,是所有程序员最熟悉判断语句,可以包含复杂条件。

缺点: 逻辑过多就会变得非常冗长难以理解。

如果不看上班和加班的表达式能快速理解什么状态对应的下班吗

switch-case

    switch(period){
        case 'afternoon':
           上班
        break;
        case 'night' && workProgress:
           下班
        break;
        case  'night' && workProgress !== 'end':
           加班
        break;
        default:
           肝就完事
        break;
    }

优点: 多种平行条件下更加简洁直观,可跳出,性能更优。

缺点: 同条件的逻辑过多就会变得像裹脚布,嵌套以及非同条件的表达式难度大。

条件变得复杂,再加一个状态(星期,周二、周四固定加班),还能维护吗

三元运算: 、短路运算 && ||

优点: 代码更加的简洁精炼

缺点: 过于复杂的三元运算语义化不强同时难以维护

    period === 'afternoon'? '上班' : (workProgress === 'end'? '下班' : '加班')

同上,条件变得复杂还能维护吗,同时看懂这串代码已经需要一点时间了。。。

策略模式/状态模式 设计模式封装

优点: 易于维护,具有一定解释性,多态

缺点: 代码量复杂度增加,需要一定抽象能力

    const AllState = {
        '上班':onWork,
        '下班':endWork,
        '加班':stillWork,
    }
    const period = 'night';
    const workProgress  = 'start';
     
    function onWork() {
        return period === 'afternoon';
    }
    
     function endWork() {
        return period === 'night' && workProgress === 'end';
    }
    
     function stillWork() {
        return period === 'night' && workProgress === 'start';
    }
    
    function getWorkState() {
        let state = '肝就对了';
        Object.keys(AllState).some((result) =>{
            if(AllState[result]()){
                state = result;
                return true;
            };
        });
        console.log(state);
        return state;
    }
    
    const workState = getWorkState();

增加了代码可维护性和可读性,支持多态,但是也增加了代码量和时间成本

语义化

你是一个新入职的员工,你的老大拍拍你的肩膀告诉你这是前面3个离职员工留下的代码

    if(state === 1){
        // 一gi窝里giaogiao
    }else if(state === 2){
        // 左边画个龙右边写个bug
    }else if(state === 3){
        //do something
    }
    .....以此类推

新人快速成长秘笈,那就是写没有接口文档,没有需求原型的项目,里面充斥着这样的数字状态的项目

以对象取代判断条件

    const stateType = {
        start: 1,  // 开始 
        doing: 2,  // 进行中
        end: 3,    // 结束
    }
    
    if(state === stateType['start']){
        //  一gigiaogiao里giaogiao
    }else if(state === stateType['doing']){
        // 左边画个龙右边写个bug
    }else if(state === stateType['end']){
        // do something
    }

不要觉得多了一个对象代码就变冗余了,这个对象中的key正是维护这份代码最好的注释

以函数取代判断条件

以一个电商活动节日为例,要求18岁以上,女生优惠比男生优惠力度大

    if(sex==='feMale' && age>18) {
        charge = (quantity * count) - 800; // 男生优惠800元
    }else{
        charge = (quantity * count)*0.8 - 500; // 女生8折再优惠500元
    }
分解条件的方式改造

1.函数取代判断条件

   function allowAge(){
       return sex==='feMale' && age>18;
   }

2.分解执行函数

    function  maleCharge(){
        return (quantity * count) - 800;
    }
    
     function  femaleCharge(){
        return (quantity * count)*0.8 - 500;
    }

3.缩成简单三元

    charge = allowAge() ? femaleCharge() : maleCharge();

执行语句都被封装起来且变得简短易懂

骚操作

1.如果有不执行的条件,为了减少不必要的代码提前return

    function allowAge(age){
         if (age<'18') throw new Error('No adult!');
         // 正常
    }

2.给参数设置默认值,这样就不需要做undefined类型处理了

     function  maleCharge(arg){ 
        const count  = arg || 5;
        return (quantity * count) - 800;
    }
    
    // 优化后
    
    function  maleCharge(count = 5){ 
        return (quantity * count) - 800;
    }

3.将多个简单条件放在数组对象里使用includes

    if(fruit === 'apple'||fruit === 'banana'||fruit === 'lemon') eatFruit()
    
    if(['apple','banana','lemon'].includes(fruit)) eatFruits()

4.当状态比较多的时候可以用这种方式来包装

es6中的Map允许使用对象来作为key

    const period = 'night';
    const workProgress = 'end';
    const worklist = () => { 
        return new Map([
          [{period:'morning',workProgress:'start'},()=>{/* 上班 */}],
          [{period:'afternoon',workProgress:'end'},()=>{/* 上班 */}],
          [{period:'night',workProgress:'doing'},()=>{/* 加班 */}],
          [{period:'night',workProgress:'start'},()=>{/* 加班 */}],
          [{period:'night',workProgress:'end'},()=>{/* 下班 */}],
          //...
        ])
    }

利用对象做为key可以存储多个数据(条件)

    function getWorkState (){
      const todayWorkState = [...worklist()].filter(([key])=>(key.period === period && key.workProgress === workProgress));
      return todayWorkState[0][1]();
    }
    const workState = getWorkState ();

如果你看完之后满脑子的加班上班,那说明你看懂了!

如果觉得不错,请素质四连,点赞、关注、转发、评论,毕竟要恰饭的嘛

了解更多,关注技术公众号:ihap 技术黑洞 !!!