一、实验目的
参考《代码中的软件工程》第六章可复用软件设计及lab5.2的源代码,完成实验并写一篇实验报告,总结Callback函数的工作机制以及通过参数进行解耦合的方法,深入理解接口设计中的抽象分层。
二、实验内容
lab5.2的源代码主要有以下4个文件。
linktableInternal.h
#include <pthread.h>
/*
* LinkTable Node Type
*/
struct LinkTableNode
{
struct LinkTableNode * pNext;
};
/*
* LinkTable Type
*/
struct LinkTable
{
struct LinkTableNode *pHead;
struct LinkTableNode *pTail;
int SumOfNode;
pthread_mutex_t mutex;
};
一个C++的头文件,主要定义了LinkTable和LinkTableNode两个结构体,其中LinkTable包含了链表的头指针、尾指针、节点数和互斥锁等信息。LinkTableNode则只包含了一个指向下一个节点的指针。
linktable.h
#ifndef _LINK_TABLE_H_
#define _LINK_TABLE_H_
#include "linktableInternal.h"
#define SUCCESS 0
#define FAILURE (-1)
/*
* LinkTable Node Type
*/
typedef struct LinkTableNode 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, void * args);
*/
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_ */
一个C++的头文件,主要定义了LinkTable和LinkTableNode两个结构体的操作函数。这个头文件定义了创建、删除、添加、删除、查找、获取链表头和获取下一个节点等操作的函数。
Linkable.c
/*
* 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 Condition(tLinkTableNode * pNode, void * args);
*/
tLinkTableNode * SearchLinkTableNode(tLinkTable *pLinkTable,
int Condition(tLinkTableNode * pNode, void * args),
void * args)
{
if(pLinkTable == NULL || Condition == NULL)
{
return NULL;
}
tLinkTableNode * pNode = pLinkTable->pHead;
while(pNode != NULL)
{
if(Condition(pNode, args) == SUCCESS)
{
return pNode;
}
pNode = pNode->pNext;
}
return NULL;
}
以上为文件中部分函数实现。实现了LinkTable头文件中定义的一些函数。其中SearchLinkTableNode函数用于从LinkTable中搜索LinkTableNode,它用到了回调函数。SearchLinkTableNode函数接受一个链表指针,一个条件函数指针,和一个参数指针作为输入。它首先检查输入是否合法,然后遍历链表,对每个节点调用条件函数,如果返回成功,就返回该节点的指针,否则继续遍历。如果没有找到满足条件的节点,就返回空指针。
menue.c
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);
}
实现了一个简单的命令行菜单程序。函数的主体部分不断接收用户输入的命令,根据输入的命令做出进一步的操作。以上是文件中的部分代码,这两个函数是用于在链表中查找一个命令的。SearchCondition函数是一个条件函数,它接受一个链表节点指针和一个参数指针作为输入,将参数指针转换为字符串类型,将链表节点指针转换为数据节点类型,然后比较数据节点的cmd字段和参数字符串是否相等,如果相等,就返回成功,否则返回失败。FindCmd函数是一个封装函数,它接受一个链表指针和一个命令字符串作为输入,调用SearchLinkTableNode函数,并将SearchCondition函数和命令字符串作为参数传递,返回一个数据节点指针。
回调函数的工作机制是通过函数指针实现的,通常在发生特定事件和满足特定条件时由另外的软件模块通过函数指针的方式调用,该过程称为对事件或者条件的响应。具体来说,一个主函数调用另一个函数时传递了一个回调函数指针,另一个函数在运行时会通过该指针调用
在lab5.2的源码中,SearchCondition函数的作为回调函数被FindCmd函数传递给了SearchLinkTableNode函数。
tDataNode* FindCmd(tLinkTable * head, char * cmd)
{
return (tDataNode*)SearchLinkTableNode(head, SearchCondition, (void*)cmd);
}
可见FindCmd函数调用了SearchLinkTableNode函数,并且在参数中指定了函数指针SearchCondition。
int SearchCondition(tLinkTableNode * pLinkTableNode, void * args)
{
char * cmd = (char*) args;
tDataNode * pNode = (tDataNode *)pLinkTableNode;
if(strcmp(pNode->cmd, cmd) == 0)
{
return SUCCESS;
}
return FAILURE;
}
该函数接受指向LinkTableNode的指针和参数指针,并返回整数值。如果条件函数返回SUCCESS,则返回节点。否则,检查下一个节点,直到到达列表的末尾。
tLinkTableNode * SearchLinkTableNode(tLinkTable *pLinkTable,
int Condition(tLinkTableNode * pNode, void * args),
void * args)
{
if(pLinkTable == NULL || Condition == NULL)
{
return NULL;
}
tLinkTableNode * pNode = pLinkTable->pHead;
while(pNode != NULL)
{
if(Condition(pNode, args) == SUCCESS)
{
return pNode;
}
pNode = pNode->pNext;
}
return NULL;
}
SearchLinkTableNode是一个搜索LinkTableNode的函数。该函数接受指向LinkTable的指针、条件函数和参数指针,并返回指向LinkTableNode的指针。
三、实验总结
在lab5.4的源码中SearchLinkTableNode函数是一个高阶函数,它接受一个函数指针作为参数,这个函数指针就是一个回调函数,它可以根据不同的需求定义不同的条件。SearchCondition函数是一个具体的回调函数,它实现了一个特定的条件,即判断数据节点的cmd字段是否和给定的字符串相等。FindCmd函数是一个封装函数,它将SearchCondition函数作为回调函数传递给SearchLinkTableNode函数,实现了在链表中查找命令的功能。
回调函数的意义在于,它可以将函数作为参数传递给另一个函数,以便在需要时调用该函数。回调函数的优点是可以增加程序的灵活性和解耦性。回调函数通常用于事件处理、异步编程、插件等场景。利用callback函数参数使Linktable的查询接口更加通用,有效地提高了接口的通用性。使用回调函数的目的是实现库函数和应用层函数的分离,使得库函数更加通用,因为可以根据需要定义不同的条件函数。
学号:502