今天看到一片博客是关于if-else的重构问题,里面提到了关于if-else的重构问题,并且说明了关于表驱动的重构方法。
- 表驱动的核心在于 逻辑与数据 分离。
- 表驱动实际就是用查表的方式去获取数据或者执行动作(函数指针)。
在程序中,数据和逻辑的添加方式和成本是不一样的,应该谨记数据是易变的,逻辑是稳定的。
1 | def contry_init(contry): |
改成表驱动法就是
1 | def contry_init1(contry): |
实际上,我们可以进一步剥离数据和逻辑的关系1
2
3
4
5
6
7
8
9
10
11
12
13def contry_init2(contry, contry_list):
if(contry in contry_list):
return contry_list[contry]
return "OTHER"
contry_list = {
"china": "CHN",
"American":"USA",
"Japan": "JPN"
}
在上面的重构的例子中,完全的将数据和逻辑分离开了,并且使用的时候更加方便,需要拓展业务或者测试不同的数据时,只需要修改关系列表即可。
表驱动法重构的优点
- 数据逻辑分离,保证在修改数据时,不会对逻辑产生影响。
- 单元测试时可以注入表格,只要数据可以转换成表,我们可以输入任意形式的数据。
- 逻辑固定写死在程序中,因为修改逻辑成本高,数据则是灵活变换的,因为修改数据成本低。
- 保证多人开发时代码的稳健性,简单的逻辑易于读懂易于维护,并且多人使用时,只用修改数据段即可,而数据本身不需要再测试。
一些实例1
2
3
4
5
6
7
8
9
10
11
12
13
14//获取星期名称
if(0 == ucDay) {
pazDayName = "Sunday";
}else if(1 == ucDay) {
pazDayName = "Monday";
}
//...
else if(6 == ucDay) {
pazDayName = "Saturday";
}
//使用表驱动改进的版本
char* pazNumChars[] = {"Sunday", "Monday", ..., "Saturday"};
char* pazDayName = pazNumChars[ucDay];
1 | //成绩分段查找, 实际上就是多个if堆叠 |
1 | // 统计字符通常用的是switch-case |
从我个人的角度上看来,其实if-else(也包括了逻辑复杂的switch-case)在代码比较少的时候,会更加简洁易懂,但是对于复杂逻辑或者说需求一直在改变的逻辑判断,当你在添加新逻辑的时候会觉得很方便,但是事后维护的时候,会发现条件覆盖是互相重叠的,并且很难读懂每一个逻辑分支,除非做很多注释。这个时候就需要做必要的重构,也就是表驱动法。实际上也不是很复杂的方法,而是很常见的方法,之前也接触过。