📃封装设计各界面的菜单
正式编写交互界面前的最后一步,封装设计各种菜单。
当前进度
.---+- include -+- tools -+- color.h
| | +- hint.h
| | +- info.h
| |
| +- user -+- user.h
| |
| +- good -+- good.h
| |
| +- order -+- order.h
| |
| +- menu -+- menu.h <---
| |
| +- config.h
|
+- src -+- tools -+- color.c
| +- hint.c
| +- info.c
|
+- data -+- id.txt
| +- user.txt
| +- good.txt
| +- order.txt
|
+- user -+- user.c
|
+- good -+- good.c
|
+- order -+- order.c
|
+- menu -+- menu.c <---
|
+- main.c菜单到底在做什么
简单来说,一个菜单做了三件事情:
告知用户有哪些选项
请用户作出一个选择
将用户的选择传给交互界面处理
我们用一个整数值表示用户的选择,菜单的基本效果如下:

一个稚嫩的实现方式
这个效果的实现其实还是蛮简单的:
上面就是主菜单的一个简单实现了,当我们写交互界面的时候只需要调用这个函数,就可以知道用户选择了第几个选项,然后选择相应的处理函数(这个处理函数需要自己来实现)来处理这个选择就可以。
但此时我们发现了两个问题:
用户的非法输入
如果用户不听话,输入了比1小或者比4大的数字怎么办?
如果用户更狂野一点,输入了
3.1415926535(小数)怎么办?如果用户疯了,输入了“F* You!"(字符串)怎么办?
有很多类似的菜单
每个菜单函数的内容都差不多,只不过是提示信息和选项个数变了。
我们需要每个菜单都写一个,总计需要写8遍几乎重复的函数吗?
优化:健体+瘦身
“健体”优化:处理用户的非法输入,提升鲁棒性
处理用户非法输入的基本思路是:
打印提示信息告诉用户它的输入是非法的
给它重新输入的机会,直到遇到合法输入为止
所以具体实现上可以这样写:
因为无论用户输入什么我们都可以认为是字符串,然后利用atoi函数将字符串转化成整型。如果转化失败或者转化的整型不在我们的选项范围内,则说明用户输入非法,我们用一个循环让用户再次输入,直到输入合法之后才能跳出循环。那么我们就解决了用户哥哥胡乱输入的问题。
这里用到了stdlib.h头文件里面的atoi函数,如果不清楚这个函数的用法的可以寻找相关教程了解一下。
参考教程: https://www.runoob.com/cprogramming/c-function-atoi.html
之后的内容中使用到这个函数的时候笔者就不再重复了。
“瘦身”优化:泛化编程,一个函数写完所有的菜单
用一个函数写,难道是一大坨if-else?当然不是。
我们现预览一下,整个项目中所需要的菜单结构如下:

其实这也是我们之后编写交互界面的结构。
我们用一个枚举类型表示这8类不同的菜单:
下面我们希望完成这样一个函数:
我们知道,枚举类型的本质其实就是无符号整数unsigned int,所以我们可以很容易的利用数组结构建立起每种菜单类型和菜单提示信息之间的映射关系。
在此之前,我们先在config.h里面约定好一共有8个菜单:
然后开始愉快的打表之旅:
其实这个表有一个高级的名字叫做哈希表(当然这个地方它哈希表的特性表现的不明显)。
这样以来,我们就可以通过menuPrompts[MAIN]来获取主菜单的提示信息,通过menuNames[MAIN]来获取主菜单的名称。
所以我们可以编写一个针对不同类型的菜单打印不同类型提示信息的函数来作为我们最终菜单函数的辅助函数:
下面还有一个问题就是每种类型的菜单选项个数是不一样的,所以我们怎么获取每种菜单的选项个数呢?
难道是一大坨if-else?当然不是。(哈哈,梅开二度)
相信你也猜到了,还是打表:
相信你也注意到了,这里没有加static关键字,其实是因为后面的交互界面模块需要用到这个optionNum,因为我们交互界面需要写多少个处理函数是从这里得到的。
此外,我们约定每种类型的菜单的最后一项都默认是返回或者退出。这个部分我们先按下不表,在后续编写交互界面的时候你就懂了。
我们暂时承认这样的设计,在menu.h中加上这个数组的声明:
综上所属,我最终给出的实现如下:
最终实现
最后我们把一个漂亮的函数声明放在头文件里面供其他模块调用就可以了,注意不要忘了添上美美的注释哈。
到此,我们封装好了一个美美的菜单函数供外部模块十分优雅而又简单得调用了。夸一下自己的代码好漂亮,我们可以进入下一站了。
最后更新于
这有帮助吗?