表驱动法是什么

表驱动法(Table-Driven Design)本质上是“以数据替代条件判断”。当规则数量不断增加、条件分支越来越难维护时,将规则整理到表中,再根据输入做查找,可以明显降低代码复杂度。

在业务系统中常见的例子包括:状态迁移表、权限矩阵、优惠券策略映射等。接下来用三个小程序演示这个思路。

案例一:石头剪刀布

传统写法往往是一串 if/elseswitch。使用表驱动法,只需维护一份胜利映射:

static const uint32_t WINNING_MOVES[] = {
    0,        // 占位,下标从 1 开始
    SCISSORS, // ROCK 胜过 SCISSORS
    PAPER,    // SCISSORS 胜过 PAPER
    ROCK      // PAPER 胜过 ROCK
};

bool player_wins(uint32_t player, uint32_t opponent) {
    if (player == opponent) {
        return false; // 平局视为未胜
    }
    return WINNING_MOVES[player] == opponent;
}

一旦要扩展玩法(例如加入“蜥蜴、斯波克”),只需补充映射表即可。

案例二:分数评级

查表法让分数转换成等级更直观:

char make_grade(uint32_t score) {
    if (score > 100) {
        return 'X';
    }
    static const char GRADES[] = {
        'F','F','F','F','F','F','D','C','B','A','A'
    };
    return GRADES[score / 10];
}

数组下标对应分段区间,使得规则一目了然。

案例三:月份天数

结合闰年判断,也可以把每月天数放进表里:

uint32_t days_in_month(uint32_t year, uint32_t month) {
    static const uint32_t DAYS_COMMON_YEAR[] = {
        31,28,31,30,31,30,31,31,30,31,30,31
    };
    static const uint32_t DAYS_LEAP_YEAR[] = {
        31,29,31,30,31,30,31,31,30,31,30,31
    };
    bool leap = (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
    return leap ? DAYS_LEAP_YEAR[month - 1] : DAYS_COMMON_YEAR[month - 1];
}

通过两份数组快速选取结果,避免在每次判断中重复写闰年的逻辑。

使用心得

  • 先画表,再写代码:把所有输入、输出、边界情况列出来,确认是否覆盖完整。
  • 表结构易于维护:使用配置文件或数据库存储规则,支持非工程师的运营同事调整。
  • 注意缓存与性能:大型表可以放在内存中,或搭配哈希索引来加速查询。
  • 保持单一职责:逻辑层只负责查表,不要混入过多的业务判断。

适用场景

  • 多条件组合导致的“条件爆炸”。
  • 规则相对稳定,但需要频繁调整内容。
  • 需要将规则交给业务人员维护。

总结

表驱动法不是新的概念,却是控制复杂度的可靠武器。将分支逻辑转化为数据结构,再通过查表解释业务规则,代码更短、更易测、更容易交接。