🖥️最后一座大山——界面

到此我们已经砖瓦皆备,只需彩线串珠了。

引子

我们已经做了许多添砖加瓦的工作,下面我们只需要把这些部分都组合起来就可以了。

不过,行百里者半九十,这一部分也是最花时间的一部分,因为这一部分需要一直保持一个整体的宏观把控,不能乱了阵脚。

交互界面适合菜单一一对应的:

交互界面结构示意图

不过在实际的开发过程中我们往往是先有顶层的架构,再添砖加瓦。因为我的教程是在读者已经有了一个自己版本的基础上写的,所以采用了自下而上的顺序,这样教程写起来会轻松一些。

根据上面的需求,我们需要实现8个交互界面,声明如下:

这个模块里面所有的8个.c文件都会包含这个头文件,因为交互界面需要喝我们之前写好的所有模块打交道,所以我们直接在头文件里面包含要用到的之前写好的所有模块。

当前进度

交互界面到底在做什么

交互界面主要做工作流程如下:

  • 通过菜单获取用户输入

  • 根据用户输入选择对应的处理函数

    这里的处理函数有可能是下一级交互界面。

  • 重复上述步骤,直到用户选择退出这一级界面

根据上述内容,我们实现一个稚嫩的主菜单。

一个稚嫩的主菜单

这里用到了optionNum数组,这也是为什么在menu.coptionNum并没有用static限制,同样也是为什么在menu.h中给了这个数组的声明。

其实按照上面的实现思路,一部分一部分慢慢写,已经可以完成这个项目的全部功能了。但这毕竟是个教学性质的项目,我希望自己的代码稍微漂亮一点。

言下之意就是实际写代码的时候能跑就行,也不管多丑陋或者多难扩展,哈哈。

上面的代码有如下一些可以优化的地方:

  • 一大坨if-else

  • 马上至少要写8遍几乎重复的代码

有没有发现和我们之前写menu的时候遇到的丑陋一摸一样,不过这次的处理方式不一样了。

优化实现方式

优化1:处理丑陋的if-else

这里,我们采用的方式和之前menu的方式一样——哈希表(其实就是打表格,建立映射关系)。

但之前我们是把字符串放在数组里面的,现在我们需要建立的是整数和函数之间的关系,函数可以放在数组里面吗?

其实是可以的,我使用了函数指针,首先在interface.h中,我们定义一个处理器类型:

所以我们可以定义这样一个数组:

于是我们的MAIN_Interface可以这样实现:

这样是不是简洁多啦。不过哪怕是这个简洁的代码,反复写8遍也够呛,所以我们下面作进一步优化。

优化2:处理重复书写的效率

这里我采用的方法是通过带参数的宏,优化相似代码重复书写的问题。

这里把TYPE分下来写是因为不和枚举类型Menu中枚举量的标识符冲突。

当我在interface.h中定义了上面的宏之后,我们mainInterface.c就可以这样写了:

是不是整个文件都简洁起来了?

这里的处理手法最好能够驻足看一下。

学过SICP的小可爱应该对Higher Order Function还有印象,C语言虽然不支持Higher Order Function,但是我们可以通过很多方法实现Higher Order Function那样的效果。

比如说之前的函数指针,实现了类似函数作为参数的功能;

这里的make_interface宏,实现了类似函数作为返回值的功能。

而高阶函数的定义:

A higher order function is a function that either takes in a function as an argument or returns a function.

我们实现了这两种效果不就相当于有了高阶函数的功能了吗?是不是感觉不管多接近底层的语言会用总是可以很强大的。😄

其实,像Python这样貌似很方便的语言,它的解释器其实也是C语言写的。

那C语言呢?

那C语言呢?

那C语言呢?

我不展开说,只举一个简单的例子。

我们使用的printf也是一个函数,但是这个函数的定义是什么呢?stdio.h头文件中装的只不过是函数的声明(所有的头文件都只能放函数的声明,放定义的话被多次包含会出现函数重定义的错误),那函数的定义到底在哪儿呢?

其实printf函数的定义不是用C语言写的,它本质上其实是用二进制(0和1)写的。我们享受了高级编程语言的红利,但总有一些底层的砖块不得不用二进制来写,然后在这些0和1组成的砖块上搭建起宏伟的大厦。

先搭骨架再填血肉

mainInterface.c中定义一个inv函数:

然后将这个函数的声明放在interface.h中,因为所有的interface模块的源文件都包含了interface.h,所以这些模块都可以使用这个inv函数。

在我们搭建骨架的时候,为了使得程序能够正常跑起来,方便调试,我们将还未实现的部分,用上面的无效处理器(这里的处理器就是之前定义的Handler类型)代替。

主交互界面

于是mainInterface.c可以这样写(把之前用省略号蒙混过关的处理器都换成inv):

是不是非常简洁,之后我们只需要用模块内的static方法来代替这些inv就可以实现我们所需要的功能了。这部分先按下不表,我们先把整体的框架全部搭好。除了直接切换交互界面以外的命令全部用inv代替。准备好接受特别舒服简洁的框架搭建过程了吗?

继续冲鸭!

管理员交互界面框架

用户交互界面框架

用户信息交互界面框架

修改用户信息交互界面框架

卖家交互界面框架

修改商品信息交互界面框架

买家交互界面框架

在之后的实现过程中,也会充分利用带参数的宏来帮助我们简化代码,可以注意一下哈。

当然,这些带参数的帮助宏有的可以在好几个交互界面里面复用,所以我们将把这些帮助宏放在interface.h头文件里面。

最后更新于

这有帮助吗?