高级软件工程lab5-----用callback增强链表模块来实现命令行菜单小程序V2.8
git地址:https://github.com/phny/lab5.git
实验步骤:
一,给lab5-1.tar.gz找bug,quit命令无法运行的bug。
1,首先将文件下载下来进行查看
编译项目并运行,发现quit命令确实是无法使用
于是仔细检查代码出现这一问题的原因,发现问题出现在linktable.c中的SearchLinkTableNode中,while循环条件while(pNode != pLinkTable -> pTail)使得函数在找到链表最后一个节点时即退出循环,因此无法访问最后一个节点.将while(pNode != pLinkTable -> pTail)改为while(pNode != NULL)即可,修改后再次编译项目运行,我们发现quit命令已经可以正常使用。
运行修改后的项目:
二,利用callback函数参数使Linktable的查询接口更加通用
具体的代码如下所示:
menu.c代码:
/************************************************************************/
/* Copyright(C) mclab.com SSE@USTC , 2017/10/20 */
/* FILENAME :menu.c */
/* PRINCIPAL AUTHOR:heyulin */
/* SUBSYSTEM NAME:mene */
/* MODULE NAME:menu */
/* LANGUAGE :C */
/* TARGET ENVIRONMENT:ANY */
/* DATE OF FIRST RELEASE:2017/10/20 */
/* DESCRIPTION:This is a program */
/************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include "linktable.h"
#define CMD_MAX_LEN 128
#define DESC_LEN 1024
#define CMD_NUM 10
void help();
void add();
void sub();
void max();
void min();
void ls();
void quit();
/* data struct and its operations */
typedef struct DataNode
{
tLinkTableNode* pNext;
char* cmd;
char* desc;
int (*handler)();
} tDataNode;
int SearchCondition(tLinkTableNode* pLinkTableNode, void* args)
{
char *cmd = (char*)args;
tDataNode * pNode = (tDataNode *)pLinkTableNode;
if(strcmp(pNode->cmd, cmd) == 0)
{
return SUCCESS;
}
return FAILURE;
}
/* find a cmd in the linklist and return the datanode pointer */
tDataNode* FindCmd(tLinkTable* head, char* cmd)
{
return (tDataNode*)SearchLinkTableNode(head, SearchCondition, (void*)cmd);
}
/* show all cmd in listlist */
int ShowAllCmd(tLinkTable* head)
{
tDataNode* pNode = (tDataNode*)GetLinkTableHead(head);
while(pNode != NULL)
{
printf("%s - %s\n", pNode->cmd, pNode->desc);
pNode = (tDataNode*)GetNextLinkTableNode(head, (tLinkTableNode*)pNode);
}
return 0;
}
int InitMenuData(tLinkTable** ppLinktable)
{
*ppLinktable = CreateLinkTable();
tDataNode* pNode = (tDataNode*)malloc(sizeof(tDataNode));
pNode->cmd = "help";
pNode->desc = "help!";
pNode->handler = help;
AddLinkTableNode(*ppLinktable, (tLinkTableNode*)pNode);
pNode = (tDataNode*)malloc(sizeof(tDataNode));
pNode->cmd = "add";
pNode->desc = "addition";
pNode->handler = add;
AddLinkTableNode(*ppLinktable, (tLinkTableNode*)pNode);
pNode = (tDataNode*)malloc(sizeof(tDataNode));
pNode->cmd = "sub";
pNode->desc = "sub";
pNode->handler = sub;
AddLinkTableNode(*ppLinktable, (tLinkTableNode*)pNode);
pNode = (tDataNode*)malloc(sizeof(tDataNode));
pNode->cmd = "ls";
pNode->desc = "list";
pNode->handler = ls;
AddLinkTableNode(*ppLinktable, (tLinkTableNode*)pNode);
pNode = (tDataNode*)malloc(sizeof(tDataNode));
pNode->cmd = "max";
pNode->desc = "max";
pNode->handler = max;
AddLinkTableNode(*ppLinktable, (tLinkTableNode*)pNode);
pNode = (tDataNode*)malloc(sizeof(tDataNode));
pNode->cmd = "min";
pNode->desc = "min";
pNode->handler = min;
AddLinkTableNode(*ppLinktable, (tLinkTableNode*)pNode);
pNode = (tDataNode*)malloc(sizeof(tDataNode));
pNode->cmd = "quit";
pNode->desc = "Quit from Menu Program V2.8";
pNode->handler = quit;
AddLinkTableNode(*ppLinktable, (tLinkTableNode*)pNode);
return 0;
}
/* menu program */
tLinkTable* head = NULL;
int main()
{
InitMenuData(&head);
/* cmd line begins */
while(1)
{
char cmd[CMD_MAX_LEN];
printf("please input a cmd >\n");
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();
}
}
}
void help()
{
ShowAllCmd(head);
}
void add()
{
double num1;
double num2;
double result;
printf("Addition!Please input two numbers!\n");
printf("Use 'Blank' or 'Enter' to divide the two numbers.\n");
scanf("%lf%lf", &num1, &num2);
result=num1+num2;
printf("%.2lf\n", result);
}
void sub()
{
double minuend;
double meiosis;
double result;
printf("subtraction!Please input two numbers!\n");
printf("Use 'Blank' or 'Enter' to divide the two numbers.\n");
scanf("%lf%lf", &minuend, &meiosis);
result=minuend-meiosis;
printf("%.2lf\n", result);
}
void mul()
{
double num1;
double num2;
double result;
printf("Multiplication!Please input two numbers!\n");
printf("Use 'Blank' or 'Enter' to divide the two numbers.\n");
scanf("%lf%lf", &num1, &num2);
result=num1*num2;
printf("%.2lf\n", result);
}
void divi()
{
double dividend;
double divisor;
double result;
printf("Divition!Please input two numbers!\n");
printf("Use 'Blank' or 'Enter' to divide the two numbers.\n");
scanf("%lf%lf", ÷nd, &divisor);
if(divisor==0)
{
printf("Error!Dividor can not be zero!\n");
}
result=dividend / divisor;
printf("%.2lf\n", result);
}
void max()
{
int a,b;
int max;
printf("please enter two integers:\n");
printf("Use 'Blank' or 'Enter' to divide the two numbers.\n");
scanf("%d",&a);
scanf("%d",&b);
if(a>b)
max=a;
else
max=b;
printf("the maximum of %d and %d is %d\n",a,b,max);
}
void min()
{
int a,b;
int min;
printf("please enter two integers:\n");
scanf("%d",&a);
scanf("%d",&b);
if(a<b)
min=a;
else
min=b;
printf("the minimum of %d and %d is %d\n",a,b,min);
}
void ls()
{
system("ls");
}
void Error()
{
printf("Command not found!\n");
}
void quit()
{
exit(0);
}
linktable.c代码:
/************************************************************************/
/* Copyright(C) mclab.com SSE@USTC , 2017/10/20 */
/* FILENAME :menu.c */
/* PRINCIPAL AUTHOR:heyulin */
/* SUBSYSTEM NAME:mene */
/* MODULE NAME:menu */
/* LANGUAGE :C */
/* TARGET ENVIRONMENT:ANY */
/* DATE OF FIRST RELEASE:2017/10/20 */
/* DESCRIPTION:This is a program */
/************************************************************************/
#include<stdio.h>
#include<stdlib.h>
#include"linktable.h"
struct LinkTable
{
tLinkTableNode *pHead;
tLinkTableNode *pTail;
int SumOfNode;
pthread_mutex_t mutex;
};
/*
* Create a LinkTable
*/
tLinkTable * CreateLinkTable()
{
tLinkTable * pLinkTable = (tLinkTable *)malloc(sizeof(tLinkTable));
if(pLinkTable == NULL)
{
return NULL;
}
pLinkTable->pHead = NULL;
pLinkTable->pTail = NULL;
pLinkTable->SumOfNode = 0;
pthread_mutex_init(&(pLinkTable->mutex), NULL);
return pLinkTable;
}
/*
* Delete a LinkTable
*/
int DeleteLinkTable(tLinkTable *pLinkTable)
{
if(pLinkTable == NULL)
{
return FAILURE;
}
while(pLinkTable->pHead != NULL)
{
tLinkTableNode * p = pLinkTable->pHead;
pthread_mutex_lock(&(pLinkTable->mutex));
pLinkTable->pHead = pLinkTable->pHead->pNext;
pLinkTable->SumOfNode -= 1 ;
pthread_mutex_unlock(&(pLinkTable->mutex));
free(p);
}
pLinkTable->pHead = NULL;
pLinkTable->pTail = NULL;
pLinkTable->SumOfNode = 0;
pthread_mutex_destroy(&(pLinkTable->mutex));
free(pLinkTable);
return SUCCESS;
}
/*
* Add a LinkTableNode to LinkTable
*/
int AddLinkTableNode(tLinkTable *pLinkTable,tLinkTableNode * pNode)
{
if(pLinkTable == NULL || pNode == NULL)
{
return FAILURE;
}
pNode->pNext = NULL;
pthread_mutex_lock(&(pLinkTable->mutex));
if(pLinkTable->pHead == NULL)
{
pLinkTable->pHead = pNode;
}
if(pLinkTable->pTail == NULL)
{
pLinkTable->pTail = pNode;
}
else
{
pLinkTable->pTail->pNext = pNode;
pLinkTable->pTail = pNode;
}
pLinkTable->SumOfNode += 1 ;
pthread_mutex_unlock(&(pLinkTable->mutex));
return SUCCESS;
}
/*
* Delete a LinkTableNode from LinkTable
*/
int DelLinkTableNode(tLinkTable *pLinkTable,tLinkTableNode * pNode)
{
if(pLinkTable == NULL || pNode == NULL)
{
return FAILURE;
}
pthread_mutex_lock(&(pLinkTable->mutex));
if(pLinkTable->pHead == pNode)
{
pLinkTable->pHead = pLinkTable->pHead->pNext;
pLinkTable->SumOfNode -= 1 ;
if(pLinkTable->SumOfNode == 0)
{
pLinkTable->pTail = NULL;
}
pthread_mutex_unlock(&(pLinkTable->mutex));
return SUCCESS;
}
tLinkTableNode * pTempNode = pLinkTable->pHead;
while(pTempNode != NULL)
{
if(pTempNode->pNext == pNode)
{
pTempNode->pNext = pTempNode->pNext->pNext;
pLinkTable->SumOfNode -= 1 ;
if(pLinkTable->SumOfNode == 0)
{
pLinkTable->pTail = NULL;
}
pthread_mutex_unlock(&(pLinkTable->mutex));
return SUCCESS;
}
pTempNode = pTempNode->pNext;
}
pthread_mutex_unlock(&(pLinkTable->mutex));
return FAILURE;
}
/*
* Search a LinkTableNode from LinkTable
* int Conditon(tLinkTableNode * pNode);
*/
tLinkTableNode * SearchLinkTableNode(tLinkTable *pLinkTable, int Conditon(tLinkTableNode * pNode,void * args), void * args)
{
if(pLinkTable == NULL || Conditon == NULL)
{
return NULL;
}
tLinkTableNode * pNode = pLinkTable->pHead;
while(pNode != NULL)
{
if(Conditon(pNode,args) == SUCCESS)
{
return pNode;
}
pNode = pNode->pNext;
}
return NULL;
}
/*
* get LinkTableHead
*/
tLinkTableNode * GetLinkTableHead(tLinkTable *pLinkTable)
{
if(pLinkTable == NULL)
{
return NULL;
}
return pLinkTable->pHead;
}
/*
* get next LinkTableNode
*/
tLinkTableNode * GetNextLinkTableNode(tLinkTable *pLinkTable,tLinkTableNode * pNode)
{
if(pLinkTable == NULL || pNode == NULL)
{
return NULL;
}
tLinkTableNode * pTempNode = pLinkTable->pHead;
while(pTempNode != NULL)
{
if(pTempNode == pNode)
{
return pTempNode->pNext;
}
pTempNode = pTempNode->pNext;
}
return NULL;
}
linktable.h代码:
/************************************************************************/
/* Copyright(C) mclab.com SSE@USTC , 2017/10/20 */
/* FILENAME :menu.c */
/* PRINCIPAL AUTHOR:heyulin */
/* SUBSYSTEM NAME:mene */
/* MODULE NAME:menu */
/* LANGUAGE :C */
/* TARGET ENVIRONMENT:ANY */
/* DATE OF FIRST RELEASE:2017/10/20 */
/* DESCRIPTION:This is a program */
/************************************************************************/
/*
* Revision log:
*
* Created by Wang Shiqing,2017/10/18
*
*/
#ifndef _LINK_TABLE_H_
#define _LINK_TABLE_H_
#include <pthread.h>
#define SUCCESS 0
#define FAILURE (-1)
/*
* LinkTable Node Type
*/
typedef struct LinkTableNode
{
struct LinkTableNode *pNext;
}tLinkTableNode;
/*
* LinkTable Type
*/
typedef struct LinkTable tLinkTable;
/*
* Create a LinkTable
*/
tLinkTable * CreateLinkTable();
/*
* Delete a LinkTable
*/
int DeleteLinkTable(tLinkTable *pLinkTable);
/*
* Add a LinkTableNode to LinkTable
*/
int AddLinkTableNode(tLinkTable *pLinkTable,tLinkTableNode * pNode);
/*
* Delete a LinkTableNode from LinkTable
*/
int DelLinkTableNode(tLinkTable *pLinkTable,tLinkTableNode * pNode);
/*
* Search a LinkTableNode from LinkTable
* int Conditon(tLinkTableNode * pNode);
*/
tLinkTableNode * SearchLinkTableNode(tLinkTable *pLinkTable, int Conditon(tLinkTableNode * pNode,void * args),void *args);
/*
* get LinkTableHead
*/
tLinkTableNode * GetLinkTableHead(tLinkTable *pLinkTable);
/*
* get next LinkTableNode
*/
tLinkTableNode * GetNextLinkTableNode(tLinkTable *pLinkTable,tLinkTableNode * pNode);
#endif /* _LINK_TABLE_H_ */
这次使用callback函数参数使得linktable查询接口更加通用,更加符合了接口设计可重用的原则与特点,符合软件工程编码规范。
在文件添加到本地的版本库中去:
提交到远程版本库中:
三,Code Review
把项目从github上clone 下来
测试项目是否能够正常运行:
四,实验总结
1,回调函数的使用是的查询链表的接口变得更加通用,同时也增加了代码的健壮性与可维护性。
2,回调函数使得程序的整个处理流程更加清晰易懂,实现了代码的可重用性与低耦合的要求
3,这次实验提高了自己的编码能力的同时,也学会了一些接口设计遵循的原则和技巧,懂得了好的代码就是要经过反复的设计和不断地优化来实现了的。