最近写了一段这样的代码。代码的功能是,通过函数function()返回的错误代码,生成一段说明该代码错误的log。
最开始我的代码是这样写的:
enum error_t{ ERROR0=0, ERROR1, ERROR2, ERROR3 }; error_t function() { //...... } int main() { error_t err_code; err_code = function(); switch(err_code) { case ERROR0: printf("Success."); break; case ERROR1: printf("Error1 occured."); break; case ERROR2: printf("Error2 occured."); break; case ERROR3: printf("Error3 occured."); break; default: printf("undefined error occured."); break; } return 0; }
这段代码简单的时候,还是可以清楚做了什么的。但是一旦error的类型比较冗长时,代码就不够清晰了。另外,这么长长的一段代码掩盖了代码的主干,颇有宣兵夺主之嫌。
每个case里面都进行printf,这是一种重复代码的行为。因为它们都进行printf,所以可以提炼出这个共同点。在导师的指导下,改成这样:
enum error_t{ ERROR0=0, ERROR1, ERROR2, ERROR3 }; char err_msg[][256] = { "Success", "Error1 occured.", "Error2 occured.", "Error3 occured.", "Undefined error occured." }; void print_err(error_t err_code) { printf("%s", err_msg[err_code]); } int main() { error_t err_code; err_code = function(); print_err(err_code); return 0; }
这样代码清晰了很多,使程序变得更加清晰。这种方法叫做表驱动法(table-driven programming)。表驱动的方法属于数据驱动编程的一种典型代表。数据驱动编程的核心出发点是相对于程序逻辑,人类更擅长于处理数据。数据驱动编程就是通过把程序逻辑的复杂度转移到人类更容易处理的数据中来,从而达到控制复杂度的目标。在上面的例子中,随着error type种类越来越多,case分支的语句会越来越长,然后整段switch的逻辑是没有变化的。通过表驱动法,将容易变化的消息和不容易变化的逻辑分离。
它不是一个全新的编程模型:它只是一种设计思路。更加丰富的内容可以看看《unix编程艺术》。我也是浅浅地了解了这么多。想了解更多表驱动编程的例子,可以点这里。