case里面两个条件_使用表驱动写出更优雅的条件判断

387b6f7eaa6b4a292382bc043ea9552c.png

在我们平时的开发中,if else是最常用的条件判断语句。在一些简单的场景下,if else用起来很爽,但是在稍微复杂一点儿的逻辑中,大量的if else就会让别人看的一脸蒙逼。
如果别人要修改或者新增一个条件,那就要在这个上面继续增加条件。这样恶性循环下去,原本只有几个if else最后就有可能变成十几个,甚至几十个。
别说不可能,我就见过有人在React组件里面用了大量的if else,可读性和可维护性非常差。(当然,这个不算if else的锅,主要是组件设计的问题)

这篇文章主要参与自《代码大全2》,原书中使用vb和java实现,这里我是基于TypeScript的实现,对书中内容加入了一些自己的理解。

从一个例子说起

日历

假如我们要做一个日历组件,那我们肯定要知道一年12个月中每个月都多少天,这个我们要怎么判断呢?
最笨的方法当然是用if else啊。

if 

这样一下子就要写12次if,白白浪费了那么多时间,效率也很低。
这个时候就会有人想到用switch/case来做这个了,但是switch/case也不会比if简化很多,依然要写12个case啊!!!甚至如果还要考虑闰年呢?岂不是更麻烦?
我们不妨转换一下思维,每个月份对应一个数字,月份都是按顺序的,我们是否可以用一个数组来储存天数?到时候用下标来访问?

const 

概念

看完上面的例子,相信你对表驱动法有了一定地认识。这里引用一下《代码大全》中的总结。

表驱动法就是一种编程模式,从表里面查找信息而不使用逻辑语句。事实上,凡是能通过逻辑语句来选择的事物,都可以通过查表来选择。对简单的情况而言,使用逻辑语句更为容易和直白。但随着逻辑链的越来越复杂,查表法也就愈发显得更具吸引力。

使用表驱动法前需要思考两个问题,一个是如何从表中查询,毕竟不是所有场景都像上面那么简单的,如果if判断的是不同的范围,这该怎么查?
另一个则是你需要在表里面查询什么,是数据?还是动作?亦或是索引?
基于这两个问题,这里将查询分为以下三种:

  1. 直接访问
  2. 索引访问
  3. 阶梯访问

直接访问表

我们上面介绍的那个日历就是一个很好的直接访问表的例子,但是很多情况并没有这么简单。

统计保险费率

假设你在写一个保险费率的程序,这个费率会根据年龄、性别、婚姻状态等不同情况变化,如果你用逻辑控制结构(if、switch)来表示不同费率,那么会非常麻烦。

if 

但是从上面的日历例子来看,这个年龄却是个范围,不是个固定的值,没法用数组或者对象来做映射,那么该怎么办呢?这里涉及到了上面说的问题,如何从表中查询?
这个问题可以用阶梯访问表和直接访问表两种方法来解决,阶梯访问这个后续会介绍,这里只说直接访问表。
有两种解决方法:
1、复制信息从而能够直接使用键值
我们可以给1-17年龄范围的每个年龄都复制一份信息,然后直接用age来访问,同理对其他年龄段的也都一样。这种方法在于操作很简单,表的结构也很简单。但有个缺点就是会浪费空间,毕竟生成了很多冗余信息。
2、转换键值
我们不妨再换种思路,如果我们把年龄范围转换成键呢?这样就可以直接来访问了,唯一需要考虑的问题就是年龄如何转换为键值。
我们当然可以继续用if else完成这种转换。前面已经说过,简单的if else是没什么问题的,表驱动只是为了优化复杂的逻辑判断,使其变得更灵活、易扩展。

enum 

这样,一旦判断条件出现了变化,这里只需要修改premiumRate里面的数据就好了。
但是觉得这个例子举得还是不够好,后续又想了一些方法来优化,将代码修改为如下会更容易理解一些。

enum 

这样修改后,结构更加清晰,也更容易维护。

索引访问表

我们前面那个保险费率问题,在处理年龄范围的时候很头疼,这种范围往往不像上面那么容易得到key。
我们当时提到了复制信息从而能够直接使用键值,但是这种方法浪费了很多空间,因为每个年龄都会保存着一份数据,但是如果我们只是保存索引,通过这个索引来查询数据呢?
假设人刚出生是0岁,最多能活到100岁,那么我们需要创建一个长度为101的数组,数组的下标对应着人的年龄,这样在0-17的每个年龄我们都储存'<18',在18-65储存'18-65', 在65以上储存'>65'。
这样我们通过年龄就可以拿到对应的索引,再通过索引来查询对应的数据。
看起来这种方法要比上面的直接访问表更复杂,但是在一些很难通过转换键值、数据占用空间很大的场景下可以试试通过索引来访问。

const ages: string[] = ['<18', '<18', '<18', '<18', ... , '18-65', '18-65', '18-65', '18-65', ... , '>65', '>65', '>65', '>65']
const ageKey: string = ages[age];

阶梯访问表

同样是为了解决上面那个年龄范围的问题,阶梯访问没有索引访问直接,但是会更节省空间。
为了使用阶梯方法,你需要把每个区间的上限写入一张表中,然后通过循环来检查年龄所在的区间,所以在使用阶梯访问的时候一定要注意检查区间的端点。

const 

阶梯访问适合在索引访问无法适用的场景,比如如果是浮点数,就无法用索引访问创建一个数组来拿到索引。
在数据量比较大的情况下,考虑用二分查找来代替顺序查找,。
在大多数情况下,优先使用直接访问和索引访问,除非两者实在无法处理,才考虑使用阶梯访问。

从这三种访问表来看,主要是为了解决如何从表中查询,在不同的场景应该使用合适的访问表。

表驱动的意义是将数据和逻辑剥离,在开发中,直接修改配置比修改逻辑要更加安全。数据的添加、删除比逻辑条件的添加、删除风险更低,数据来源也更加灵活。

引用知乎大V Ivony的一段话:

分析和阅读一段代码的时候,很多时候是有侧重面的,有时候侧重于数据,有时候侧重于逻辑。假设我们有这样一个需求,当某某值小于100时,就如何如何。那这个里面的100就是数据,当需求变更为某某值小于200时,才如何如何,那么我们关注的点在于这个数据的修改。而不是整个逻辑的修改,数据的剥离,有助于我们更快的发现修改点和修改代码。

欢迎关注我的微信公众号:

验证​mp.weixin.qq.com

参考资料:

  1. 代码大全(第2版)
  2. 这个例子中的if else也要重构掉吗?
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值