像我这样毫无能力又没有色彩的人
也就只能在失去中成长了吧

面向可配置UI组件,减少if else

#js

前言

阅读本文,你可能可以了解到如何利用面向对象的思想,如何基于可配置化的思路,优雅摒弃if else的完成多状态业务。

发现最近有两篇文章写得很好分别都是讲如何优化业务中的if else代码,本人在去年刚刚好也有类似的重构领悟,借此热度一并分享给大家。

一个小例子

我有一个哥们二黄,他有很多的女盆友,每周末不同的时间段约会对象都不同,他的时间表如下。

如果我们翻译成代码可能大多数人都会写出这样的样子。

if(早上) {
    if(周六) {
        // 红红
    } else if(周日) {
        // 圆圆
    }
} else if(中午) {
    if(周六) {
        // 琪琪
    } else if(周日) {
        // 佳佳
    }
} else if(晚上) {
    if(周五) {
        // 菲菲    
    } else if(周六) {
        // 莉莉
    } else if(周日) {
        // 桐桐
    }
}

如果我们还需要新增几个女盆友,那这个if恐怕让人看得想杀人了。在此,仔细思考下,曾经你写过的活动页面,曾经你写过的多状态业务,是否也造出了如此丑陋的恶魔呢?

如果我们把上面的例子换成一个按钮,不同状态有不同的文案并且又有不同的操作那依旧可以写成如下恶心的代码

if(早上) {
    if(周六) {
        this.btnText = '约红红';
        this.btnHandler = function 吃饭(){}
    } else if(周日) {
        this.btnText = '约圆圆';
        this.btnHandler = function 看电影(){}
    }
} else if(中午) {
    if(周六) {
        this.btnText = '约琪琪';
        this.btnHandler = function 散步(){}
    } else if(周日) {
        this.btnText = '约佳佳';
        this.btnHandler = function 运动(){}
    }
} else if(晚上) {
    if(周五) {
        this.btnText = '约菲菲';
        this.btnHandler = function 逛街(){}
    } else if(周六) {
        this.btnText = '约莉莉';
        this.btnHandler = function 探险(){}
    } else if(周日) {
        this.btnText = '约桐桐';
        this.btnHandler = function 不可描述(){}
    }
}

如果我们又要加一点东西,比如如果不同的对象二黄需要穿不同的衣服,鞋子。那又可能再每个if里增加。

真实的场景

state-3

这个状态图就是我们去年的某一个活动页面,一个页面里有顶部tip文案提示,悬浮按钮,进度提示,提现按钮,提现弹窗文案等等等。不同的UI分别会根据不同用户身份, 不同状态的变化而变化。相信你们也有过类似的经历,不难发现跟我们上面那简单的例子非常得相似。

一不小心就可能写出恶心的if else代码。比如这样的

state-1

还有这样的

state-2

是不是看着就想吐?可维护性差,可读性也差!

这时候其实我们可以转换下思路,把我们每一个多状态的UI当做一个对象,进行配置。

首先,我们从后台拿到的数据可能是这样。

{
  user: 0, // 0 会长 1 会员 2 非会员
  club: 'ing', // ing 招募中 success 招募成功 fail 招募失败
};

然后我们尝试为我们的【进度提示】进行配置一下。通过我们前面的状态图可以发现进度提示其实只有文案没有操作,所以我们可以这样。

state-machine-4

不难发现,其实我们用对象的形式解决了if else,每一个ifelse if其实就是对象多嵌套一层而已。

第三个options则可以让我们很方便的传入不同的状态对UI进行拓展,比如我们这有个人数的变量。

最终我们在所需要这UI的地方直接processTip(data.user, data.club, { num: x }).text即可获取文案。把这样一行代码替换了之前的if else在业务代码中会显得十分清晰,不用在意内部是如何实现的。当产品抽风想各种修改添加状态的时候,我们也仅仅需要配置一下即可。不需要改动任何业务代码。如果UI变化了,直接删去调用的地方,完全不用担心删错了某个if else导致逻辑出错。

我们再举一个例子,比如【悬浮按钮】。我们的UI既可能有文案同时还会有相应的操作。我们也可以进行配置化。

我们可以给UI添加handler方法。同样在我们相应的业务代码里就能这样写fixedBtn(data.user, data.club).handler()

再或者,你的UI可能点击前的状态和点击后的也不一样。那我们可以利用AOP的思想配置after。大致如下

0: {
    'ing': {
        text: '邀请好友加入读书会',
        handler() { ... },
        after() {
            return '分享成功';
        },
    }
}

在本文中,我所需要的状态只有userclub,然而可能大家会有更复杂的状态,其实我们还能按一定的规则写一个适配器。比如这样

const adapter = (user, club) => `${user}-${club}`;

那我们的配置可以更简洁一点

const state = {
  '0-ing': {
    text: `还差${num}人即招募成功`,
  },
  '0-success': {
    text: `招募成功,目前成员数${num}人`,
  },
  '0-fail': {
    text: `还差${num}人即招募成功`,
  },
  '1-ing': {
    text: `还差${num}人报名即可成功创办读书会`,
  },
  '1-success': {
    text: `创办成功,目前成员数${num}人`,
  },
  '1-fail': {
    text: `还差${num}人即创办成功`,
  },
}

尾声

至此,我们面向可配置UI的介绍就接近尾声了。个人觉得借助这种方法,无论是再复杂的状态,我们都可以通过该方式,重构邪恶的if else写出可读性高的,可维护性强的优雅业务代码。