本博文内容是课程最后的总结。
参考:
1、 软件工程(C语言实践篇)学习心得总结
2、 软件工程(C编码实践篇)学习总结
一 软件设计方法论(原则)
- 概述:
- 模块化
- 接口
- 信息隐藏
- 增量开发
- 抽象
- 一般化
模块化:
将一个系统拆分成多个模块,要求低耦合、高内聚,减少出错提高可维护性。接口:
信息隐藏通过接口的定义来完成。信息隐藏越多,耦合性越低。增量开发:
以模块化为基础;明确的接口定义为增量开发提供可能。
补充,拆分模块,增量开发时打破依赖关系:抽象:
同层级放在函数调用前,子层级放在函数调用内部。
二 软件设计具体内容
1 代码设计规范
- 用控制结构、数据结构来简化代码。
- 一定要有错误处理。
- 一个函数或方法、一个系统、子系统、模块、类等只做一件事情。
- goto 常用来做错误处理(C 语言)。
2 可重用模块的接口设计
- 接口定义内容:
- 函数
- 参数
- 返回值
- 接口的五个要素:
- 目的:如函数名。
- 前置条件:如接口函数调用前已经做好哪些准备工作(插入链表元素前需要先创建链表)。
- 协议:格式方式。如参数、返回值的类型,指针指向的数据格式。
- 后置条件:如返回值、printf 函数接口的效果是屏幕显示了字符串。
- 质量属性:如接口函数执行时间限制在 1 秒内。
- 接口的分类:
- 共享数据或变量名(间接的接口)
- call-in functions(最常见,函数调用的方式)
- callback functions(用函数指针作为卧底,调用上层模块的函数)
- 同步调用接口(阻塞式的函数调用,专门等待要使用的函数)
- 异步调用接口(调用后继续做自己的事情,直到被调用的函数过来。用信号量、消息队列等方式实现)
- 可重用模块的接口设计:
一般方法定义的接口:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
typedef struct linkTableNode
{
struct linkTableNode *pNext;
}tLinkTableNode;
typedef struct linkTable
{
tLinkTableNode *pHead;
tLinkTableNode *pTail;
int sumOfNode;
}tLinkTable;
tLinkTable *createLinkTable();
int deleteLinkTable(tLinkTable *pLinkTable);
int addLinkTableNode(tLinkTable *pLinkTable,tLinkTableNode *pNode);
int delLinkTableNode(tLinkTable *pLinkTable,tLinkTableNode *pNode);
tLinkTableNode *getLinkTableHead(tLinkTable *pLinkTable);
tLinkTableNode *getNextLinkTableNode(tLinkTable *pLinkTable,tLinkTableNode *pNode);用 callback 方式定义的接口:
使 Linktable 的查询接口更加通用,隐藏接口信息。将数据的定义放到 linktable.c 文件中。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
typedef struct LinkTableNode
{
struct LinkTableNode pNext;
}tLinkTableNode;
typedef struct LinkTable tLinkTable;
tLinkTable * CreateLinkTable();
int DeleteLinkTable(tLinkTable *pLinkTable);
int AddLinkTableNode(tLinkTable *pLinkTable,tLinkTableNode * pNode);
int DelLinkTableNode(tLinkTable *pLinkTable,tLinkTableNode * pNode);
tLinkTableNode * SearchLinkTableNode(tLinkTable *pLinkTable, int Conditon(tLinkTableNode * pNode, void * args),void *args);
tLinkTableNode * GetLinkTableHead(tLinkTable *pLinkTable);
tLinkTableNode * GetNextLinkTableNode(tLinkTable *pLinkTable,tLinkTableNode * pNode);
- 接口定义内容:
补充:不同部分隔两行区分。参考 Nginx 头文件。
- 版权等信息
- #ifndef、#define
- 包含的头文件
- 宏定义
- 结构体定义
- 变量定义
- 函数声明
3 函数的可重入性、线程安全
4 子系统可重用设计
给整个命令行菜单程序定义一套调用接口。参考代码。
- 概念:
- 对于菜单程序,不会像链表模块那样很多地方要用到,需要考虑通用性、线程安全。不需要太具体,也不要太通用。够用就好。
- MenuConfig 函数相当于初始化一个链表,向内添加结点等。
- ExecuteMenu 函数就是菜单程序的主程序。
子系统接口示例:
1
2
3
4
5.h 文件:
int MenuConfig(char *cmd, char *desc, int (*handler)());
int ExcuteMenu();调用子系统示例:
1
2
3
4
5
6
7
8
9
10.c 文件:
int main(int argc, char **argv)
{
MenuConfig("version", "xxx v1.0(Menu program v3.0.0 inside)", NULL);
MenuConfig("argtest", "test arg option", argtest);
MenuConfig("quit", "quit from xxx", Quit);
ExcuteMenu();
return 0;
}