2017 Fall Software Engineering Learning (3)
模块化 可复用 命令行菜单
公开发表一篇实验报告,并在实验报告中注明【网易云课堂昵称 + 《软件工程(C编码实践篇)》MOOC课程作业http://mooc.study.163.com/course/USTC-1000002006 】
一、实验需求分析
- 参照实验2
- 注意代码的业务逻辑和数据存储之间的分离,即将系统抽象为两个层级:菜单业务逻辑和菜单数据存储
- 要求:1)遵守代码风格规范,参考借鉴代码设计规范的一些方法;2)代码的业务逻辑和数据存储使用不同的源文件实现,即应该有2个.c和一个.h作为接口文件。
二、程序具体实现
不多说,直接上代码:
先是源文件截图:
可以明显看到
- 主程序 —- main.c
- 数据存储 —- dataclass.c dataclass.h
- 业务逻辑 —- operation.c operation.h
/**************************************************************************************************/
// >File Name: main.c
// >Author: AcrididCheng
// >Mail: chenghuaming@aliyun.com
/**************************************************************************************************/
#include <stdio.h>
#include "dataclass.h"
#include "operation.h"
#define CMD_MAX_LEN 128
int main()
{
printf("Input a command, type help to see the commands!\n");
while(1)
{
char cmd[CMD_MAX_LEN];
scanf("%s",cmd);
tDataNode *p = findCmd(head, cmd);
if(p == NULL)
{
printf("Wrong input, Input again!\n");
continue;
}
//printf("%s - %s\n", p->cmd, p->desc);
if(p->handler != NULL)
{
p->handler();
}
}
return 0;
}
对比之前的方法,这里运用了函数指针加链表的方法使得增添命令的时候不用修改主程序,而且没有命令数量的限制。
#pragma once
#include "dataclass.h"
int Help();
int Exit();
int Version();
static tDataNode head[] =
{
{"help", "this is a help command", Help, &head[1]},
{"version", "this is a version command", Version, &head[2]},
{"exit", "exit program", Exit, NULL}
};
业务逻辑层的头文件。
具体命令在这里修改就行了。
/**************************************************************************************************/
// >File Name: operation.c
// >Author: AcrididCheng
// >Mail: chenghuaming@aliyun.com
/**************************************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include "dataclass.h"
#include "operation.h"
int Help()
{
showAllCmd(head);
return 0;
}
int Exit()
{
printf("Thank you for using menu program, bye~~^_^\n");
exit(0);
}
int Version()
{
printf("This is version 1.0\n");
return 0;
}
相应的具体功能实现。
#pragma once
typedef struct DataNode
{
char* cmd;
char* desc;
int (*handler)();
struct DataNode *next;
} tDataNode;
tDataNode* findCmd(tDataNode* head, char* cmd);
int showAllCmd(tDataNode* head);
dataclass.h,这个头文件里面定义了与数据有关的操作,包括结构体的声明。
/**************************************************************************************************/
// >File Name: dataclass.c
// >Author: AcrididCheng
// >Mail: chenghuaming@aliyun.com
/**************************************************************************************************/
#include <stdio.h>
#include <string.h>
#include "dataclass.h"
tDataNode* findCmd(tDataNode* head, char* cmd)
{
if (head == NULL || cmd == NULL)
{
return NULL;
}
tDataNode *p = head;
while(p != NULL)
{
if( strcmp(p->cmd, cmd) == 0)
{
return p;
}
p = p->next;
}
return NULL;
}
int showAllCmd(tDataNode* head)
{
if( head == NULL )
{
printf("wrong usage of showAllCmd()\n");
return -1;
}
printf("Menu List:\n");
tDataNode* p = head;
while( p != NULL )
{
printf("%s -- %s\n", p->cmd, p->desc);
p = p -> next;
}
return 0;
}
同理,函数的具体实现。
上述代码模块化的就非常好,而且相对于之前的代码显然质量更高,更易读。
*有关项目编译与makefile编写,参见上次的博客
三、实验结果展示
以下进行一下实验展示,省略git步骤,有兴趣的可以自己clone下我的工程
先编译,再运行
运行完后清理一下项目
至此实验就结束了。
四、实验总结
这次实验是真正意义上第一次用软件工程方法的实践。首先是模块化和复用性,这些大家都知道需要做到,但不知道如何去做。这次实验以一个简单的命令行小程序,告诉我们从data层次上来配置程序,而不是传统的用顺序方法去配置程序,使得程序更新时操作容易,而且程序易读性好。