周立功

可动态扩展多分支选项的实现(5)

0
阅读(4346)

1.1.1 利用查表调用函数

通过之前介绍的范例可以看出,“打印的帮助信息”同样也不能做到动态绑定,同时还需要用手工添加case语句及处理函数。可想而知其扩展性很差,这是开发过程中最容易被忽略的问题。

1.   高级声明

或许,上面的示例都过于简单,下面不妨来一点刺激的。我们可以先声明一个结构体类型,并同时定义一个结构体变量,然后再定义一个结构体数组,其示例如下:

1       #define HELP_LEN 64                                // 函数说明的最大长度

2       #define TABLE_LEN 10                               // 函数表中的最大的函数个数

3

4       typedef struct CmdEntry{

5               void (*pfuncmd)();                         // 定义函数指针,用于接收函数的入口地址

6               char cHelp[HELP_LEN];

7       }CmdEntry;

8      

9       static CmdEntry cmdArray[TABLE_LEN] = {           // 定义结构体数组(函数表)并初始化

10            {&CreateFile, "新建文件"},                  // 取CreatFile()函数地址,帮助信息

11            {&OpenFile, "打开文件"},                    // 取OpenFile()函数地址,帮助信息

12            {&SaveFile, "保存文件"},                    // 取SaveFile()函数地址,帮助信息

13            // <标注1>在这里添加函数

14            {0, 0}                                      // 退出

15     };

     注意:在这里定义数组的长度是为了方便下一个版本添加调用函数。

 

     2.   利用查表调用函数

     根据上面的定义,即可用以下方式获得函数的入口地址。

         cmdArray[iCmdNum].pfuncmd

然后用函数指针回调相应的功能函数,其示例如下:

         cmdArray[iCmdNum].pfuncmd();

由此可见,如果采用回调函数法,且以动态绑定的方式,则程序的可扩展性得到了很大的提升,因为我们只需在“<标注> 1”处注册自定义的函数,无需多处修改代码,不仅可以很好地解决程序的可扩展性问题,而且还大大地降低程序的出错几率,详见程序清单1.1。

程序清单1.1  控制台菜单选项程序(V0.5)

1       #include <stdio.h>

2       #include <stdlib.h>

3

4       #define HELP_LEN 64                            // 函数说明的最大长度

5       #define TABLE_LEN 10                           // 函数表中最大的函数个数

6

7       typedef struct CmdEntry{                       // 定义函数结构体

8               void (*pfuncmd)();                     // 接收函数入口地址的函数指针

9               char cHelp[HELP_LEN];

10     }CmdEntry;

11

12     void ShowHelp();

13

14     // 此处省略CreateFile()函数代码                 // “新建文件”菜单

15     // 此处省略OpenFile()函数代码                   // “打开文件”菜单

16     // 此处省略SaveFile()函数代码                   // “保存文件”菜单

17

18     static CmdEntry cmdArray[TABLE_LEN] = {         // 定义函数表

19           {&CreateFile, "新建文件"},

20           {&OpenFile, "打开文件"},

21           {&SaveFile, "保存文件"},

22           // <标注1>在这里添加函数

23           {0, 0}

24     };

25

26     void ShowHelp()                                // 显示函数表中的内容

27     {

28         int i;

29

30         for (i = 0; (i < TABLE_LEN) && cmdArray[i].pfuncmd; i++) {

31                 printf("%d\t%s\n", i, cmdArray[i].cHelp);

32         }

33     }

34

35     void CmdRunning()

36     {

37         int iCmdNum;

38             

39         while (1){

40               ShowHelp();                  // “帮助信息”显示初始化

41               printf("请选择!\n");

42               iCmdNum = getchar() - '0';  // 将字符转换为数字,转换失败也可以

43               fflush(stdin);              // 清空缓冲区

44               if (iCmdNum >= 0 && iCmdNum < TABLE_LEN && cmdArray[iCmdNum].pfunCmd){

45                      cmdArray[iCmdNum].pfunCmd();

46               }

47               else{

48                        printf("对不起,你选择的数字不存在,请重新选择!\n");

49               }

50          }

51     }

52     void main()

53     {

54         CmdRunning();

55     }

这种方式相对来说其扩展性有了很大的提升,只需要在“<标注1>”处注册自定义的函数即可,帮助显示函数ShowHelp()会自动地显示新增加的函数帮助信息,因为修改一处比修改多处出错的可能性要低得多。

1.1.2    提供通用接口
如果采用上述方式注册函数,则必须在这个文件的“<标注1>”处修改源代码。很多时候,当需要扩展菜单功能时,而又不允许随意修改源码,怎么办?唯一的解决方法就是为系统增加一个可动态扩展的接口函数,详见程序清单1.2。

程序清单1.2  动态扩展接口函数(V0.6)

1       void AddCmd(CmdEntry cmdentry)

2       {

3                int i;

4

5                for (i = 0; (i < TABLE_LEN) && cmdArray[i].pfuncmd; i++) {

6                          ;                          // 找到空的功能条目位置

7                }

8                if (TABLE_LEN == i) {

9                          printf("Sorry,table is full!");

10              }

11              else {

12                        cmdArray[i] = cmdentry;

13              }

14     }

当以后需要扩展菜单项时,那么只需要调用AddCmd接口即可。不过这里还是有一个不足之处,当菜单项的数量达到定义的TABLE_LEN长度后,就不能继续添加菜单项了,请读者思考一下怎样才能做到动态增长。