表格驱动编程在代码中的应用

1. 毕业设计中的使用

第一次使用表格驱动编程,是在大学毕业设计的时候。做一个LL(1)的词法分析程序,需要读取终结符、非终结符、以及推导公式。程序会根据以上信息生成FIRST集合和LAST集合,然后根据递归下降分析的方法,就可以判断当前的表达式,是否符合对应的文法规则了。因为在代码中对于推导用到的表格没有写为Hard Code(写死,硬编码),所以可以适应很多的符合规则的文法。

2. 开源代码中的使用

第二次遇到表格驱动编程的方法,是在LXGJ,做jpg图像的压缩,用的是jpeg2000。当时还写了一篇文章,总结了一下,具体见 http://www.cnblogs.com/Dennis-mi/p/3871860.html 。这里的代码采用的是动态生成表格的方式,在函数中通过宏来控制表格的大小,来实现动态的支持不同图片格式的解析。

3. 工作中使用

在LXGJ的时候,领导布置了一个任务,需要用户选择从不同的来源生成PDF,大致有扫描、文件、图片……,需要做成一个类似于Eclipse(Visual Studio)生成工程的向导类似的。然后明白了需求以后,我就开始写代码了。然后项目经理过来问我做的怎么样了,我把写了好多 if else的代码拿给他看,他说“你怎么能这么写呢,万一需求有变化,怎么改。

代码类似于

if( SCAN == type )
{
    dlg = new ScanDialog();
    dlg.show();
    if(DPI300 == dlg.getDpi())
    {
        .....
    }
}
else if(FILE == type)
{ 
    ...
}
else if(IMAGE == type)
{
    ....
}

以上的代码在遇到规则有变化,或者每一步方式的选项要增加或者减少的时候,修改比较麻烦。
项目经理的建议

Dialog choiceDialog[4][5]=
{
   new ScanDialog(),new DpiDialog(),new xxDialog(),NULL,
   new FileDialog(),new xxDialog(),NULL,NULL,
   new ImageDialog(),new DpiDialog(),new ImageTypeDialog(),new xxDialog(),NULL,
   NULL,NULL,NULL,NULL,NULL,
}

采用下面的这种写法具有更好的可扩展性和灵活性。如果需要增加一种方式,则可以增加一行,如果需要增加方式中的一个选项,则可以在某行的某列后面增加一个dialog,用NULL表示结束,可以每种方式经过的选项不同。

4.我的新发现

在LXSJ的时候,我们需要对APP登录做验证,验证的过程比较多而且规则有可能会变化。这个时候采用表格驱动编程是一种很好的方式。举例说明如下:
比如我们需要验证

1、用户是否余额--->有余额允许登录,没有不允许;
2、用户是否是VIP会员--->是会员允许登录,不管其他条件;
3、用户如果是合作方的用户--->则合作方决定是否允许登录;
4、用户如果已经是老用户--->是允许登录,不是不允许登录;

此时可以采用老办法,用if else来写。但是新的方法是采用表格驱动,具体如下。
表格的每一项对应一个判断函数,返回 bool 值。

loginLogicTable[][] =
{
   /*当前需要进行的判断,成功时的状态,失败时的状态*/
   LOGIN_START,IS_VIP,IS_VIP,
   IS_VIP,LOGIN_SUCCEED,HAS_BALANCE,
   HAS_BALANCE,LOGIN_SUCCEED,IS_REGULAR_CUSTOMER,
   IS_REGULAR_CUSTOMER,LOGIN_SUCEED,IS_PARTER_CUSTOMER,
   IS_PARTER_CUSTOMER,SEND_REQUEST_TO_PARTER,LOGIN_FAILED,
   RECIVE_PARTER_RESPONSE,LOGIN_SUCCEED,LOGIN_FAILED,
}

以上是此时规则的描述,其中 LOGIN_SUCCEEDLOGIN_FAILED 是最终的状态。
第一项对应着一个函数,使用这个表格的伪代码如下

bool isLoginSucceed(userInfo)
{
    curState = LOGIN_START;
    while( curState != LOGIN_SUCCEED && curState != LOGIN_FAILED)
    { 
        changeArray = findArray(curState); //返回loginLoginTable中的一行
        if(callFunc(changeArray[0])
        { 
            curState=changeArray[1];
        }
        else
        {
           curState = changeArray[2];
        } 
    }
    return curState == LOGIN_SUCCEED;
}

如果不需要判断是否是合作伙伴的用户了。
则将

IS_REGULAR_CUSTOMER,LOGIN_SUCEED,[IS_PARTER_CUSTOMER], 
//改为
IS_REGULAR_CUSTOMER,LOGIN_SUCEED,[LOGIN_FAILED],

就可以了,改动非常的方便。
如果要增加一个判断,比如要增加xyz指令判断。
则修改如下:

IS_REGULAR_CUSTOMER,[LOGIN_SUCEED],IS_PARTER_CUSTOMER,
IS_PARTER_CUSTOMER,SEND_REQUEST_TO_PARTER,LOGIN_FAILED,
RECIVE_PARTER_RESPONSE,LOGIN_SUCCEED,LOGIN_FAILED,
/*改为*/
IS_REGULAR_CUSTOMER,[JUDGE_XYZ_CMD],IS_PARTER_CUSTOMER,
IS_PARTER_CUSTOMER,SEND_REQUEST_TO_PARTER,LOGIN_FAILED,
RECIVE_PARTER_RESPONSE,[JUDGE_XYZ_CMD],LOGIN_FAILED,
[JUDGE_XYZ_CMD],LOGIN_SUCCEED,LOGIN_FAILED,

可以看出,此时的修改很小,而且逻辑很清楚。

原文地址:https://www.cnblogs.com/Dennis-mi/p/8612698.html