2017 Fall Software Engineering Learning (5)
用callback增强链表模块 命令行菜单
公开发表一篇实验报告,并在实验报告中注明【网易云课堂昵称 + 《软件工程(C编码实践篇)》MOOC课程作业http://mooc.study.163.com/course/USTC-1000002006 】
一、实验需求分析
- 给lab5-1.tar.gz找bug,quit命令无法运行的bug
- 利用callback函数参数使Linktable的查询接口更加通用
- 注意接口的信息隐藏
二、程序排错
这次的题目相对来说轻松许多,因为不用自己写代码,只用在给的模板上进行修改。
首先是给程序找bug,这个bug其实很容易找,其他命令都没错,只有quit这个命令出错,而quit刚好位于尾节点。同时我们发现,使用help命令时,能够打印quit这个节点的介绍,这意味着程序并不是在创建链表的时候出现的错误。那就意味着肯定是在搜索命令的时候出错了,我们找到搜索函数:
tLinkTableNode * SearchLinkTableNode(tLinkTable *pLinkTable, int Conditon(tLinkTableNode * pNode))
{
if(pLinkTable == NULL || Conditon == NULL)
{
return NULL;
}
tLinkTableNode * pNode = pLinkTable->pHead;
while(pNode != pLinkTable->pTail)
{
if(Conditon(pNode) == SUCCESS)
{
return pNode;
}
pNode = pNode->pNext;
}
return NULL;
}
我们发现,循环的判断语句是pNode == 链表尾节点的时候退出,那么在pNode指向尾节点的时候并没有执行循环内的操作,所以就会返回NULL,这样,尾节点的命令就没办法找到了。所以我们稍作修改:
tLinkTableNode * SearchLinkTableNode(tLinkTable *pLinkTable, int Conditon(tLinkTableNode * pNode,void *arguments),void *arguments)
{
if(pLinkTable == NULL || Conditon == NULL)
{
return NULL;
}
tLinkTableNode * pNode = pLinkTable->pHead;
while(1)
{
if(Conditon(pNode,arguments) == SUCCESS)
{
return pNode;
}
if (pNode == pLinkTable->pTail)
break;
pNode = pNode->pNext;
}
return NULL;
}
先执行循环,再执行判断操作。原始但是管用的操作。
三、程序接口通用化和隐藏
这个就像老师视频中所说的,用callback来通用化接口,也就是用函数指针来作为参数。优点在于连接口具体内容都可以用户自定义,接口变得更为抽象。
关键代码:
tLinkTableNode * SearchLinkTableNode(tLinkTable *pLinkTable, int Conditon(tLinkTableNode * pNode,void *arguments),void *arguments)
{
if(pLinkTable == NULL || Conditon == NULL)
{
return NULL;
}
tLinkTableNode * pNode = pLinkTable->pHead;
while(1)
{
if(Conditon(pNode,arguments) == SUCCESS)
{
return pNode;
}
if (pNode == pLinkTable->pTail)
break;
pNode = pNode->pNext;
}
return NULL;
}
int SearchCondition(tLinkTableNode * pLinkTableNode,void *arguments)
{
tDataNode * pNode = (tDataNode *)pLinkTableNode;
char *cmd = (char*)arguments;
if(strcmp(pNode->cmd, cmd) == 0)
{
return SUCCESS;
}
return FAILURE;
}
以上是函数定义,我们将SearchCondition()函数作为接口参数给传到SearchLinkTableNode()函数中,具体代码:
tDataNode* FindCmd(tLinkTable * head, char * cmd)
{
return (tDataNode*)SearchLinkTableNode(head,SearchCondition,cmd);
}
tDataNode *p = FindCmd(head, cmd);
这种实现方法非常好,不过它作为一种思想,我也没什么其他可以介绍的了,记住这种好方法就是了。
再就是根据实验说的,将结构体具体内容放到.c文件里,因为.h是暴露给用户的,对于用户要尽量少的暴露具体细节。
四、实验总结
这次实验工作量不大,但是内容非常有意义,着重介绍了可重用的一个小细节,接口的通用性。而且通过这个代码的阅读,我发现了许多我原本程序的不足,尤其是我感觉到了链表的各种具体实现上的区别,还有将功能独立的代码模块封装成一个函数这些思想,而不是仅仅只在于功能文件化。
五、结果展示
这次因为每有什么独立编程的内容,只是修改代码,所以没什么好展示的:
各个功能模块正常,没啥其他的。
重点在于代码!!!这是我的代码,有兴趣可以去看看