表驱动方法编程

表驱动方法编程(Table-Driven Methods)是一种编程模式,适用场景:消除代码中频繁的if else或switch case的逻辑结构代码,使代码更加直白.

扩展链接:if else或switch case的逻辑结构

用例分析

假设让你实现一个返回每个月天数的函数(为简单起见不考虑闰年)。

复制代码

public static int getDayByMonth(int month){
  if(month<1|| month>12){
    throw new RuntimeException("month invalid parameter:"+month);
  }
  if(month==1||month==3||month==5||month==7||month==8||month==10||month==12){
    return 31;
  }else if(month==4||month==6||month==9||month==11){
    return 30;
  }else{
    return 28;
  }
}

复制代码

仔细发现每月天数无外乎 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),假定转换关系:

ScoreDegree
[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 函数可以保持一行都不改动。

posted @ 2019-03-07 11:13  等价交换原则  阅读(802)  评论(0编辑  收藏  举报