表驱动方法编程(Table-Driven Methods)是一种编程模式,适用场景:消除代码中频繁的if else或switch case的逻辑结构代码,使代码更加直白.
用例分析
假设让你实现一个返回每个月天数的函数(为简单起见不考虑闰年)。
仔细发现每月天数无外乎 28、30、31 三种,或许会用 switch-case 利用case穿透:
public static int getDayByMonth(int month){
if(month<1|| month>12){
throw new RuntimeException("month invalid parameter:"+month);
}
switch (month) {
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
return 31;
case 2:
return 28;
case 4:
case 6:
case 9:
case 11:
return 30;
default:
return -1;
}
}
这两种方法充斥了大量的逻辑判断,还凭空冒出了一大堆1,2,...,11,12这样的 Magic Number
表驱动处理起来就赏心悦目得多了:
static int monthDays[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
public static int getDayByMonth(int month){
if(month<1|| month>12){
throw new RuntimeException("month invalid parameter:"+month);
}
return monthDays[(month- 1)];
}
表驱动法编程的使用与访问方式:
①直接访问通过索引/key值来直接访问数组/集合
如:array[index]参考上面的月份获取天数的案例
或者map.get("传入的key值")集合获取
②间接通过索引/key值来直接访问数组/集合
100 种商品,每种商品都有一个 ID 号,但很多商品的描述都差不多,所以只有 30 条不同的描述,如何建立建立商品与商品描述的表?
方法是建立一个 100 长的索引和 30 长的描述,然后这些索引指向相应的描述(不同的索引可以指向相同的描述),这样就解决了表数据冗余的问题啦。
struct product_t { char * id; int desc_index; }; const char * desc[] = { "description_1", "description_2", ... "description_29", "description_30" }; const product_t goods [] = { {"id_1", 3}, {"id_2", 1}, ... {"id_99", 12}, {"id_100", 5} }; const char* desc_product (const char* id) { for (const product_t & p : goods) { if (strcmp(p.id, id) == 0) { return desc[p.desc_index - 1]; } } return NULL; }
③阶梯访问
例子:将百分制成绩转成五级分制(我们用的优、良、中、合格、不合格,西方用的 A、B、C、D和F),假定转换关系:
Score | Degree |
---|---|
[90-100] | A |
[80,90) | B |
[70,80) | C |
[60,70) | D |
[0,60) | F |
const char gradeTable[] = { 'A', 'B', 'C', 'D', 'F' }; const int downLimit[] = { 90, 80, 70, 60 }; int degree(int score) { int gradeLevel = 0; char lowestDegree = gradeTable[sizeof(gradeTable)/sizeof(gradeTable[0]) - 1]; // 这里可用二分查找优化 while (gradeTable[gradeLevel] != lowestDegree) { if(score < downLimit[gradeLevel]) { ++ gradeLevel; } else { break; } } return gradeTable[gradeLevel]; }
将来如果等级规则变了(比如 85~100 分为等级 A,或添加 50~60 分为等级 E),只需要修改 gradeTable 和 downLimit 表(也可通过配置文件形式)就行,degree 函数可以保持一行都不改动。