使用atcon告别混乱的if else

这一篇,来聊聊混乱的if、else是怎么来的,怎么和他们说再见,以及写的一个工具函数atcon

一、代码是怎样混乱的

最简单的一个需求:根据活动改变二维码

1
qrcode = 'img_a';

// 活动二
if (activity === 2) {
    qrcode = 'img_b';
// 活动三
} else if (activity === 3) {
    qrcode = 'img_c';
// 活动四
} else if (activity === 4) {
    qrcode = 'img_d';
// 活动五
} else if (activity === 5) {
    qrcode = 'img_e';
// 活动六
} else if (activity === 6) {
    qrcode = 'img_f';
}

嗯,这个逻辑看起来还是挺简单的。这时候,我们增加一个变量notice,因为需要根据活动改变文案,而且只在某些特殊情况变更,于是它可能变成了这样子:

1
qrcode = 'img_a';
notice = '文案一';

// 活动二
if (activity === 2) {
    qrcode = 'img_b';
// 活动三
} else if (activity === 3) {
    qrcode = 'img_c';
    notice = '文案三';
// 活动四
} else if (activity === 4) {
    qrcode = 'img_d';
// 活动五
} else if (activity === 5) {
    qrcode = 'img_e';
    notice = '文案五';
// 活动六
} else if (activity === 6) {
    qrcode = 'img_f';
}

看起来也没什么。但是这时候,产品跑来说,活动下线了,二维码需要做区分。于是代码可能又变成了这样子:

1
qrcode = 'img_a';
notice = '文案零';

// 活动二
if (activity === 2) {
    qrcode = 'img_b';
    if (isEnd) {
        qrcode = 'img_h';
    }

// 活动三
} else if (activity === 3) {
    qrcode = 'img_c';
    notice = '文案三';
    if (isEnd) {
        qrcode = 'img_i';
    }
// 活动四
} else if (activity === 4) {
    qrcode = 'img_d';
    if (isEnd) {
        qrcode = 'img_j';
    }
// 活动五
} else if (activity === 5) {
    qrcode = 'img_e';
    notice = '文案五';
    if (isEnd) {
        qrcode = 'img_k';
    }
// 活动六
} else if (activity === 6) {
    qrcode = 'img_f';
    if (isEnd) {
        qrcode = 'img_l';
    }
}

if (isEnd) {
    notice = '下线了';
}

是不是开始有点懵逼了?Hold on,隔了几天,产品又跑来跟我们说,活动三、四、五效果不是特别好,我们准备改一版样式,做下ABTEST看看效果。

这意味着我们的代码里又要增加一个变量,于是我们只能说,卧槽。

二、如何改造

或者说,如何在一开始给自己留好后路。

我们先从第1步重新开始,如果用对象保存这些关联,是不是就会简单很多呢。

1
imgMap = {
    '2': 'img_b',
    '3': 'img_c',
    '4': 'img_d',
    '5': 'img_e',
    '6': 'img_f'
};
qrcode = imgMap[activity] || 'img_a';

第2步,增加的notice,其实和qrcode没有任何关联。

1
noticeMap = {
    '3': '文案三',
    '5': '文案五'
};
notice = noticeMap[activity] || '文案零';

活动下线区分,我们稍稍改变下结构好了,几分钟的事。

1
imgMap = {
    online: {
        '2': 'img_b',
        // .....etc.
    },
    offline: {
        '2': 'img_h',
        // .....etc.
    }
};
var noticeMap = {
    online: {
        '3': '文案三',
        '5': '文案五'
    },
    offline: '下线了'
};
qrcode = imgMap[isEnd ? 'offline' : 'online'][activity] || 'img_a';

if (isEnd) {
    notice = noticeMap.offline;
} else {
    notice = noticeMap.online[activity] || '文案零';
}

上面,我们的编码方式,整体还是挺不错的,每种状态都差不多能对应一个结果。

但是,有没有发现,我们在notice的处理上,又开始写if、else了。

这里先不提,我们继续下面一步。

如果只是对在线的活动二、三做abtest,并改变里面的二维码和图片,我们该怎么做呢?

如果采用上面imgMap一一对应的关系,数据结构就变成了这样子:

1
imgMap = {
    online: {
        '2': {
            a: 'img_b',
            b: 'img_o'
        },
        '3': {
            a: 'img_b',
            b: 'img_p'
        },
        '4': {
            a: 'img_c',
            d: 'img_c'
        },
        // .....etc.
        
    },
    offline: {
        '2': {
            a: 'img_h',
            b: 'img_h'
        },
        '3': {
            a: 'img_b',
            b: 'img_b'
        },
        '4': {
            a: 'img_c',
            d: 'img_c'
        },
        // .....etc.
    }
};

有没有发现,为了保证我们可以一次性访问到具体对应的值,我们做了非常多重复配置

我们当然不希望进行重复配置,而是希望处理的结构可以像这样简单:

var noticeMap = {
online: {
‘2’: {
a: ‘abtest文案二’
},
‘3’: {
a: ‘abtest文案三’,
b: ‘文案三’
},
‘5’: ‘文案五’
},
offline: ‘下线了’
};

但是我们怎么提取对应的值,又如何避免提取的过程不会出错呢。

像上面notice一样,我们只能写if、else判断代码,外加循环判断对应属性的值是否存在,好像又要重走以前的老路。

三、使用 atcon

atcon就是用来解决这个问题,封装了属性值提取、reduce循环判断等处理,让大家可以拥有清晰美妙的结构的同时,也能非常方便地提取对应条件分支的值。

使用方式:

1
imgMap = {
    online: {
        '2': {
            a: 'img_b',
            b: 'img_o'
        },
        '3': {
            a: 'img_b',
            b: 'img_p'
        },
        '4': 'img_c',
        '5': 'img_d',
        '6': 'img_e'
    },
    offline: {
        '2': 'img_h',
        '3': 'img_i',
        '4': 'img_j',
        '5': 'img_k',
        '6': 'img_l'
    },
    __DEFAULT__: 'img_a'
};

noticeMap = {
    online: {
        '2': {
            a: 'abtest文案二'
        },
        '3': {
            a: 'abtest文案三',
            b: '文案三'
        },
        '5': '文案五'
    },
    offline: '下线了'
    __DEFAULT__: '文案零'
};

isImg = img => img.search(/\.(png|jpg)$/) !== -1;
isString = str => Object.prototype.toString.call(str) === '[object String]';

qrcode = atcon(imgMap, [isEnd ? 'offline' : 'online', activity, isAbtest ? 'a' : 'b'], isImg);
notice = atcon(noticeMap, [isEnd ? 'offline' : 'online', activity, isAbtest ? 'a' : 'b'], isString);

atcon将我们一开始的默认值,也通过__DEFAULT__的提取,做了指定。因此,复杂的分支判断,只要先通过细致设想的结构,然后与atcon完美地结合起来,就可以开始和if else说再见了。

四、结构变更

如果这时候,需要把所有abtest的a,文案全部改成一样的,而下线的文案保持跟上线的文案一致,我们甚至能够直接改变noticeMap结构。

1
noticeMap = {
    b: {
        '3': '文案三',
        '5': '文案五'
    },
    a: '一样的ABTEST文案',
    __DEFAULT__: '文案零'
};
notice = atcon(noticeMap, [isAbtest ? 'a' : 'b', activity], isString);

我们改变思路、结构的同时,改变atcon第二个数组参数的数组项位置和数量,就达到了调整复杂if else逻辑的目的。

五、最后

我们不改逻辑,基本不改代码,我们只改配置。

从现在起,告别混乱的if else

具体见:atcon


思路来源