实验三:内部模块化的命令行菜单小程序V2.0
【shawn520 + 《软件工程(C编码实践篇)》MOOC课程作业http://mooc.study.163.com/course/USTC-1000002006 】
【代码仓库】https://github.com/shawn520/SE.git
GitHub代码链接
实验要求
注意代码的业务逻辑和数据存储之间的分离,即将系统抽象为两个层级:
菜单业务逻辑和菜单数据存储
要求:
1)遵守代码风格规范,参考借鉴代码设计规范的一些方法;
2)代码的业务逻辑和数据存储使用不同的源文件实现,即应该有2个.c和一个.h作为接口文件。
实验目的
通过设计简单的命令行菜单细小程序,了解并掌握程序内部模块化设计的思想,实现菜单数据存储与菜单业务逻辑的分离.
实验思路
用一个静态链表来存储可变化的菜单命令及其对应函数,在主函数中实菜单命令逻辑
将数据结构的定义、函数声明放到linklist.h中,声明的函数在linklist.c中实现,在menu.c中定义main函数。共实验help、quit、version、四则运算arithmetic、输出当前系统时间time、判断两个数的大小judge、求一个数的倒数、给一列数排序。
实验过程
在一个头文件中定义链表数据结构及其操作原型,并在相应源文件中实现其操作;然后在主函数源文件中实现菜单命令。
进入实验目录并创建menu.c作为业务逻辑模块
git clone https://github.com/shawn520/SE.git
mkdir lab3
cd lab3
vim menu.c
在menu.c中添加如下代码
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "linklist.h"
#define CMD_MAX_LEN 128
#define DESC_LEN 1024
#define CMD_NUM 10
/*menu program*/
static tDataNode head[] =
{
{"help", "this is help cmd!", Help, &head[1]},
{"version", "menu program v2.0", NULL, &head[2]},
{"quit", "Quit from menu", Quit, &head[3]},
{"add", "Addtion", Add, &head[4]},
{"sub", "Substraction", Sub, &head[5]},
{"Mul", "Multition", Mul, &head[6]},
{"Div", "Division", Div, NULL}
};
int Hello()
{
printf("Hi~My name is Shawn.\nThank you for use my cmd prgram!\n");
}
int main()
{
/* cmd line begins */
while(1)
{
char cmd[CMD_MAX_LEN];
printf("Input a cmd number>");
scanf("%s", cmd);
tDataNode *p = FindCmd(head, cmd);
if(p == NULL)
{
printf("This is a wrong cmd!\n");
continue;
}
printf("%s - %s\n", p->cmd, p->desc);
if(p->handler != NULL)
{
p->handler();
}
}
}
int Help()
{
ShowAllCmd(head);
return 0;
}
编辑linklist.h,作为数据存储模块,增加可重用性
vim linklist.h
/**************************************************************************/
/* Copyright (C) mc2lab.com, SSE@USTC, 2017-2018 */
/* */
/* FILE NAME : menu.c */
/* PRINCIPAL AUTHOR : ShawnLiu */
/* SUBSYSTEM NAME : menu */
/* MODULE NAME : menu */
/* LANGUAGE : C */
/* TARGET ENVIRONMENT : ANY */
/* DATE OF FIRST RELEASE : 2017/10/07 */
/* DESCRIPTION : This is a menu program */
/**************************************************************************/
/*
* Revision log:
*
* Created by ShawnLiu, 2017/10/07
*
*/
/* data struct and its operations */
typedef struct DataNode
{
char* cmd;
char* desc;
int (*handler)();
struct DataNode *next;
}tDataNode;
/* find a cmd in the linklist and return the datanode pointer */
tDataNode* FindCmd(tDataNode * head, char * cmd);
/* show all cmd in listlist*/
int ShowAllCmd(tDataNode * head);
int Help();
int Quit();
int Add();
int Sub();
int Mul();
int Div();
再定义存储所有cmd的linklist数据结构, 并声明其操作方法FindCmd(), 以及遍历输出linklist中所有节点的ShowAllCmd():
vim linklist.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "linklist.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)
{
printf("Menu List:\n");
tDataNode *p = head;
while(p != NULL)
{
printf("%s - %s\n", p->cmd, p->desc);
p = p->next;
}
return 0;
}
int Quit()
{
exit(0);
}
int Add()
{
double num1, num2;
double sum;
printf("Addtion. Please input two numbers:\n");
scanf("%lf %lf", &num1, &num2 );
sum = num1 + num2;
printf("%lf + %lf = %lf\n", num1, num2, sum);
return 0;
}
int Sub()
{
double num1, num2;
double result;
printf("Subtraction. Please input two numbers:\n");
scanf("%lf %lf", &num1, &num2 );
result = num1 - num2;
printf("%lf - %lf = %lf\n", num1, num2, result);
}
int Mul()
{
double num1, num2;
double result;
printf("Multiplication. Please input two numbers:\n");
scanf("%lf %lf", &num1, &num2 );
result = num1 * num2;
printf("%lf * %lf = %lf\n", num1, num2, result);
}
int Div()
{
int num1, num2;
double result;
printf("Division. Please input two numbers:\n");
scanf("%d %d", &num1, &num2 );
if(num2==0)
{
printf("Error: divisor can not be zero!\n");
}
else
{
result = num1 / num2;
printf("%d / %d = %lf\n", num1, num2, result);
}
}
编译得到可执行文件
gcc menu.c linklist.c -o menu
并测试menu程序的功能,如图所示
完成后提交到git
git add menu.c linklist.h linklist.c
git commit -m 'menu_v2.0'
git push
复审实验
本实验的代码可通过如下方式下载并运行
git clone https://github.com/shawn520/SE.git
cd lab3
gcc menu.c linklist.c -o menu
./menu
实验心得及体会:
软件=程序+软件工程
程序=算法+数据结构
经过基本Modularity (also called separation of concerns)设计的代码
模块化的思想和命令行菜单的实现范例(开源社区中常见的写法)
基本模块化的写法
代码设计中的一些常见方法
- KISS(keep it simple & stupid)
- using design to frame the code(matching design with implementation)
- including pseuducode
- 不要和陌生人说话原则
- 合理利用Control Structures、Data Structures来简化代码
- 一定要有错误处理