你还在用if/else吗?
前提
我们在日常开发当中经常会遇到复杂的条件判断,一般的做法是用if/else,或者优雅一点的写法是用switch语句来实现多个条件的判断,这样的话会有很多问题,随着判断条件的增加,代码中if/else或switch会变得越来越臃肿,如何使用更优雅的方式来实现呢,本文带你来试一下。
案例
先看一段代码
通过代码可以看到这个按钮的点击逻辑:根据不同活动状态做两件事情,发送日志埋点和跳转到对应页面,大家可以很轻易的提出这段代码的改写方案,switch出场:
这样看起来比if/else清晰多了,细心的同学也发现了小技巧,case 2和case 3逻辑一样的时候,可以省去执行语句和break,则case 2的情况自动执行case 3的逻辑。
这是我们日常开发中基本大多数同学的写法,这种写法可以吗,当然可以,只是不够优雅,这几天在看左耳听风的专栏,在读到编程的本质一章的时候,文中有这样一个观点任何算法都会有两个部分, 一个是 Logic 部分,这是用来解决实际问题的。另一个是 Control 部分,这是用来决定用什么策略来解决问题。Logic 部分是真正意义上的解决问题的算法,而 Control 部分只是影响解决这个问题的效率。程序运行的效率问题和程序的逻辑其实是没有关系的。我们认为 ,如果将 Logic 和 Control 部分有效地分开,那么代码就会变得更容易改进和维护,那么下面我们就用这种思想去试着分离一下代码,可以分离成如下形式
这样的形式其实就是DSL(Domain Specific Language)+一个DSL的解析器,这样,DSL 的描述是“Logic”,而我们的onButtonClick则成了Control,代码大大简化了。由此我们可以总结出如下的思想:
好的继续我们的优化,是不是还有其他写法呢?有的:
我们需要把问题升级一下,以前按钮点击时候只需要判断status,现在还需要判断用户的身份:
这了我不写每个判断里的具体逻辑了,因为代码太冗长了。 原谅我又用了if/else,因为我看到很多人依然在用if/else写这种大段的逻辑判断。 从上面的例子我们可以看到,当你的逻辑升级为二元判断时,你的判断量会加倍,你的代码量也会加倍,这时怎么写更清爽呢?
上述代码核心逻辑是:把两个条件拼接成字符串,并通过以条件拼接字符串作为键,以处理函数作为值的Map对象进行查找并执行,这种写法在多元条件判断时候尤其好用。
当然上述代码如果用Object对象来实现也是类似的:
如果有些同学觉得把查询条件拼成字符串有点别扭,那还有一种方案,就是用Map对象,以Object对象作为key:
是不是又高级了一点点?
这里也看出来Map与Object的区别,Map可以用任何类型的数据作为key。
我们现在再将难度升级一点点,假如guest情况下,status1-4的处理逻辑都一样怎么办,最差的情况是这样:
好一点的写法是将处理逻辑函数进行缓存:
这样写已经能满足日常需求了,但认真一点讲,上面重写了4次functionA还是有点不爽,假如判断条件变得特别复杂,比如identity有3种状态,status有10种状态,那你需要定义30条处理逻辑,而往往这些逻辑里面很多都是相同的,这似乎也是笔者不想接受的,那么这里要怎么处理呢,可以使用正则表达式,如下:
这里Map的优势更加凸显,可以用正则类型作为key了,这样就有了无限可能,假如需求变成,凡是guest情况都要发送一个日志埋点,不同status情况也需要单独的逻辑处理,那我们可以这样写:
也就是说利用数组循环的特性,符合正则条件的逻辑都会被执行,那就可以同时执行公共逻辑和单独逻辑,因为正则的存在,你可以打开想象力解锁更多的玩法,本文就不赘述了。
总结
本文的核心就是讲逻辑(Logic)和Control(控制)如何分离,如果所有的程序都能够很好的分离,那么代码的可维护性就会大大提高,因为代码不仅仅要运行还要写来给人看的,这也是编程的意义。