表格驱动法优化ifelse
表格驱动的常见形式
开发中,常常会用到各种分支逻辑,if
else
switch
case
霸屏,如下代码。
1 | if (state == 1) { |
在编程的世界,只有两种元素组成,那就是数据与代码。像上面的代码,估计大家一看,除了 state
的 {1, 2...}
是数据的概念,其他的诸如 if
else
process1()
process2()
processByDefault()
自然也就化为代码的部分。
而代码和数据的划分,不是泾渭分明的,你写的任何代码,在编译器的角度,又成为了数据。
因此如果我们换一个角度思考上述代码,重新定义数据,可能就会得到另一个结果。 如下
1 | Map<Integer, Runnable> stateProcessMap = new HashMap<>{{ |
提出了 stateProcessMap
的数据,表示 state
与业务的映射关系。
那数据的提出有什么好处呢?
显而易见的是,以后同类结构的修改,你不再是改 if
else
代码的部分,而只需要修改 stateProcessMap
数据部分。
可不要小看这一点点小改动,虽然现在 stateProcessMap 概念是数据,而载体还是在代码上,但可是动静分离的基础。
阶梯式的表格驱动
在常见形式中,key 一般为静态的数据,能够用在 if (x == xx)
等于的部分,而如果面对如下的代码,可能又得继续思考了。
1 | if (distance > 3) { |
上述逻辑,由于是阶梯式的逻辑,必须先从最大的数开始判断,所以无法使用常规的形式,你可以这样重新定义数据,如下:
1 | int[] distanceLevels = {3, 2, 1}; |
上述形式,我们称为阶梯访问表,虽然不如常见形式简介,但也达到了分离了数据与代码的效果,取得了更高的扩展性。
NavigableMap 在表格驱动中阶梯逻辑的使用
在上述的阶梯访问表中,也存在几个问题:
- 大量数据时,存在线性访问,查找成本较大
- 需要手工保证 key -> value 的映射
应运而生的,我们想到可以使用 TreeMap,即是 key -> value 的结构,又是有序的结构。
1 | NavigableMap<Integer, Supplier> distanceAvgMap = new TreeMap<> {{ |
NavigableMap
是一个可导航的 Map
接口, 上层接口为 SortedMap
,顶层接口为 Map
,TreeMap
是 NavigableMap
的具体实现,是一个平衡二叉树的实现,主流的实现是八股文中的红黑树,其中 lowerEntry(key)
是以二分查找法查找一个小于或等于 key
的 Entry
。
这样,我们阶梯式的逻辑,就用一个二叉树的 Map
数据结构表示成了数据形式,与阶梯访问表相比,更优雅的达到了数据与代码分离的效果。