原文转载于:http://blog.sina.com.cn/s/blog_40965d3a0101eaka.html
概述:用表驱动替代冗长的逻辑语句,遵循元编程思想。
表驱动法(Table driven method),是一种设计模式,可以用来代替复杂的if/else或switch-case逻辑判断。
假设要设计一个函数,该函数的功能是获得每个月的天数,如下:
int GetMonthDays(int iMonth)
{
int iDays;
if (1 == iMonth){iDays = 31;}
else if (2 == iMonth){iDays = 28;}
else if (3 == iMonth){iDays = 31;}
else if (4 == iMonth){iDays = 30;}
else if (5 == iMonth){iDays = 31;}
else if (6 == iMonth){iDays = 30;}
else if (7 == iMonth){iDays = 31;}
else if (8 == iMonth){iDays = 31;}
else if (9 == iMonth){iDays = 30;}
else if (10 == iMonth){iDays = 31;}
else if (11 == iMonth){iDays = 30;}
else if (12 == iMonth){iDays = 31;}
return iDays;
}
本来应该是一件很简单的事情,代码却这么冗余,同时这也导致了代码可读性的降低。更好的方法是使用表驱动,如下:
static int s_nMonthDays[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
int GetMonthDays(int iMonth)
{
return s_nMonthDays[iMonth - 1];
}
可以明显看出,用了一些存储空间作为代价,代码清晰了很多,效率也提升了。
那么,表驱动方法为什么优于“函数封装或宏”?
简短的switch-case或if/else用起来还是比较顺手的,所以还是继续用吧。但是对于分支太多的,最好想办法将其化解开。
化解长switch-case的方法有很多,是选择函数封装、宏,还是表驱动?看下面的示例:
//版本1 – case分支版
int ProcessControl(UINT function_no, void* para_in, void* para_out)
{
int result;
switch (function_no)
{
case PROCESS_A:
result = ProcessA(para_in, para_out);
break;
case PROCESS_B:
result = ProcessB(para_in, para_out);
break;
case PROCESS_C:
result = ProcessC(para_in, para_out);
break;
//...
default:
result = UN_DEFINED;
break;
}
return result;
}
int ProcessA(void* para_in, void* para_out){...}
int ProcessB(void* para_in, void* para_out){...}
int ProcessC(void* para_in, void* para_out){...}
//...
上面这种实现方式,分支越多,可读性越差,维护起来也越麻烦。最主要的是,它不够优雅。
//版本2 – 宏定义版
#define DISPATCH_BEGIN(func) switch (func)\
{
#define DISPATCH_FUNCTION(func_c, function) case func_c:\
return = function(para_in, para_out);\
break;
#define DISPATCH_END(code) default:\
result = code;\
}
int ProcessControl(UINT function_no, void* para_in, void* para_out)
{
int result;
DISPATCH_BEGIN(function_no)
DISPATCH_FUNCTION(PROCESS_A, ProcessA)
DISPATCH_FUNCTION(PROCESS_B, ProcessB)
DISPATCH_FUNCTION(PROCESS_C, ProcessC)
//...
DISPATCH_END(UN_DEFINED)
return result;
}
这个版本的代码稍微有了点清爽的感觉。但是用宏是治标不治本的方法。
//版本3 – 表驱动版
typedef struct tagDispatchItem
{
UINT func_no;
ProcessFuncPtr func_ptr;
}DISPATCH_ITEM;
DISPATCH_ITEM dispatch_table[MAX_DISPATCH_ITEM];
int ProcessControl(UINT function_no, void* para_in, void* para_out)
{
int i;
for (i=0; i<MAX_DISPATCH_ITEM; i++)
{
if (function_no == dispatch_table[i].func_no)
return dispatch_table[i].func_ptr(para_in, para_out);
}
return UN_DEFINED;
}
上面采用的是数组形式,还可以换为高级的数据结构,如下所示:
//版本4 – 表驱动版(高级数据结构)
typedef std::hash_map<UINT, ProcessFuncPtr> CmdHandlerMap;
CmdHandlerMap HandlerMap;
void InitHandlerMap()
{
HandlerMap[PROCESS_A] = reinterpret_cast<ProcessFuncPtr>(&ProcessA);
HandlerMap[PROCESS_B] = reinterpret_cast<ProcessFuncPtr>(&ProcessB);
HandlerMap[PROCESS_C] = reinterpret_cast<ProcessFuncPtr>(&ProcessC);
//...
}
int ProcessControl(UINT function_no, void* para_in, void* para_out)
{
CmdHandlerMap::iterator it = HandlerMap.find(function_no);
if (it != HandlerMap.end())
{
ProcessFuncPtr pHandler = it->second;
return (*pHandler)(para_in, para_out);
}
return UN_DEFINED;
}
孰优孰劣,一看便知。