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