提高C++代码质量 - 用表驱动取代冗长的逻辑选择

原文转载于: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;

}

孰优孰劣,一看便知。

猜你喜欢

转载自blog.csdn.net/business122/article/details/81351876