用`表驱动`对代码里的if-else来一次`马杀鸡`
什么是表驱动编程
表驱动 是在表中查找信息,而不使用 if-else 或者 switch 语句。
表驱动-马杀鸡
往往我们追求的简单逻辑就是使用逻辑语句直接描述流程,但是往往随着逻辑语句的复杂,使得我们需要考虑用表驱动替代
例如 if-else 和 switch
if (type == 1) {
create();
} else if (type == 2) {
update();
} else if (type == 3) {
query();
} else {
delete();
}
switch(type) {
case 1:
create();
break;
case 2:
update();
break;
case 3:
query();
break;
default:
delete();
break;
}
这两种方法充斥着大量的逻辑判断,也就是 逻辑 & 数据 混杂。而表驱动的方式 核心就是解构 逻辑 与 数据,将 代码 和 数据 划分清楚。
而表驱动的编程方式包含了访问方式有两种
- 直接通过索引访问
static Runnable[] runnables = {
() -> {create();},
() -> {update();},
() -> {query();},
() -> {delete();}
};
public static void main(String[]args){
runnables[0].run();
}
- 间接通过索引访问
Map<Integer, Runnable> runnables = new HashMap<>{
put(1, this::create);
put(2, this::update);
put(3, this::query);
put(4, this::delete);
}
public static void main(String[]args){
int step = 1;
runnables.get(step).run();
}
从上面可以看出,我们借助表驱动的方式,不管是直接还是间接都消除了大篇幅的 if-else 或 switch 语句。而且再后续的业务变更的话,我们只需要更改 runnables
数据部分,这样实现了 逻辑 与 数据 的分离。
阶梯访问的表驱动
上面的 if-else 或 switch 还是比较简单的,但是也存在 if-else-if-else , 像这种阶梯阶段的模式
if (score > 80) {
return veryGood();
} else if (score > 60) {
return good();
} else {
return bad();
}
我们在转化该种 代码 和 数据 的时候,往往是先从最大的数值开始的,这样的话,我们不能像上面一样直接用索引做映射,需要额外多一层转化
int[] scores = {80, 60};
Supplier[] methods = {this::veryGood, this::good};
for(int i = 0; i < scores.length; i++) {
if(scores[i] > score) {
return methods[i].get();
}
return bad();
}
回头我们再看这种转换,确实做到了 代码 和 数据 的分割,不太常见但是确实是表驱动实现的一种方式。因为我们重新定义了数据。
阶梯式表驱动 升华
回溯上面的的阶梯访问的表驱动,仔细阅读一下是否存在问题呢?
- 大量数据环境下,因为是线性访问,查询成本较高。【不像间接索引实现的访问使用 Map 】
- 需要
手动
保证 key 到 索引,索引再到 value 的映射 【从大到小(当前的实现下)】
如果要解决以上的问题,我们可以参考上面间接访问的采用Map
,因为考虑要有序,所以我们可以联想到使用 TreeMap
, 它是 key-value 结构且有序。
深究 TreeMap
, 其上层接口是 NavigableMap
, NavigableMap
继承了 SortedMap
, 从TreeMap
接口的注释能看出,
// A Red-Black tree based NavigableMap implementation.
// The map is sorted according to the natural ordering of its keys, or by a Comparator provided at map creation time, depending on which constructor is used.
TreeMap
是基于一个平衡二叉树的实现。 因为其具体实现了 NavigableMap
, 看 lowerEntry
方法,是以二分查找法查找一个小于或等于 key
的 Entry
, 这样就解决了上述的大数据量的查找问题。
而且因为是 key-value 模式, 我们不需要隐性的手动去维护索引关系。
/**
* Returns a key-value mapping associated with the greatest key
* strictly less than the given key, or {@code null} if there is
* no such key.
*
* @param key the key
* @return an entry with the greatest key less than {@code key},
* or {@code null} if there is no such key
* @throws ClassCastException if the specified key cannot be compared
* with the keys currently in the map
* @throws NullPointerException if the specified key is null
* and this map does not permit null keys
*/
Map.Entry<K,V> lowerEntry(K key);
所以我们再一次优化后, 这样我们参照 通过间接索引访问,将 阶梯式表驱动访问 也使用 TreeMap
改造了,从结果看,这种比上面线性访问,更能体现 数据 和 代码 分离的效果.
TreeMap<Integer, Supplier> methodMaps = new TreeMap<> {{
put(80, this::veryGood);
put(60, this::good);
}}
methodMaps.lowerEntry(score).getValue().get();
结语
瑞思拜!Enjoy!