lab5.2的文件结构为:linktableInternal.h被linktable.h引用,linktable.h被linktable.c引用,linktable.c被menu.c引用。
linktableinternal.h:申明了两个结构体LinkTable和LinkTableNode。LinkTable结构体是链表;LinkTableNode是链表结点,指向下一个链表指针的节点。
linktable.h:引用linktableinternal.h,定义了结构体tLinkTableNode,tLinkTable,并且在该文件中定义了上述两个结构体的创建链表、删除链表、添加链表节点、删除链表节点、查找链表节点、获取链表头结点、获取链表尾结点。
linktable.c:引用linktable.h,对linktable.h中的定义进行了具体的实现
menu.c:引用linktable.c,LinkTable链表中的节点是与业务无关的,数据与业务分离,实现最终功能。
回调函数是通过函数指针调用的函数。回调函数会把函数的指针(即地址)作为参数传递给另一个函数,当这个指针调用其所指向的函数时,就称这是回调函数。回调函数不是实现该函数的软件模块直接调用,而是在特定的事件或条件发生时由另外的软件模块通过函数指针的方式调用,用于对该事件或条件进行响应,是一种下层软件模块调用上层软件模块的特殊方式。
给Linktable增加Callback方式的接口,需要两个函数接口,一个是call-in方式函数,如SearchLinkTableNode函数,其中有一个函数作为参数,这个作为参数的函数就是callback函数,如代码中Conditon函数。在软件编程中,我们希望软件是松散耦合,所以我们会给接口SearchLinkTableNode(call-in方式)增加参数,callback函数Condition也增加参数,该参数的作用是传输用户输入的命令(如help、version、quit)。
/*
* Search a LinkTableNode from LinkTable
* int Conditon(tLinkTableNode * pNode);
*/
tLinkTableNode * SearchLinkTableNode(tLinkTable *pLinkTable, int Conditon(tLinkTableNode * pNode));
接口的代码:
/*
* Serach 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;
}
可以看到代码中没有Condition函数的代码实现,因为Condition是用户代码传进来的一个函数指针,只是我们在回调函数接口定义时规定了传进来的函数指针的规格。
在menu.c中采用Callback方式的函数接口,menu.c中调用SearchLinkTableNode的地方FindCmd函数:
int SearchCondition(tLinkTableNode * pLinkTableNode, void * args)
{
char * cmd = (char*) args;
tDataNode * pNode = (tDataNode *)pLinkTableNode;
if(strcmp(pNode->cmd, cmd) == 0)
{
return SUCCESS;
}
return FAILURE;
}
int main()
{
InitMenuData(&head);
/* cmd line begins */
while (1)
{
char cmd[CMD_MAX_LEN];
printf("Input cmd > ");
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();
}
}
return 0;
}
可以看到函数调用这一接口时Condition函数传递的是SearchCondition,SearchCondition函数返回值是tLinkTableNode *类型,FindCmd函数调用时把返回值转成了tDataNode*类型。
menu.c中的主函数:InitMenuData(&head)初始化head(tLinkTable *类型),再在FindCmd中传递head参数
int InitMenuData(tLinkTable ** ppLinktable)
/* find a cmd in the linklist and return the datanode pointer */
tDataNode* FindCmd(tLinkTable * head, char * cmd)
{
return (tDataNode*)SearchLinkTableNode(head, SearchCondition, (void*)cmd);
}
整合所有需要的关键代码:
/*
* LinkTable Node Type
*/
typedef struct LinkTableNode tLinkTableNode;
/*
* LinkTable Type
*/
typedef struct LinkTable tLinkTable;
/*
* LinkTable Node Type
*/
struct LinkTableNode
{
struct LinkTableNode * pNext;
};
/*
* LinkTable Type
*/
struct LinkTable
{
struct LinkTableNode *pHead;
struct LinkTableNode *pTail;
int SumOfNode;
pthread_mutex_t mutex;
};
/*
* Create a LinkTable
*/
tLinkTable * CreateLinkTable()
/*
* Add a LinkTableNode to LinkTable
*/
int AddLinkTableNode(tLinkTable *pLinkTable, tLinkTableNode * pNode)
经过代码分析,head通过CreateLinkTable创建了一个tLinkTable*类型数据,一个链表头指针;然后,AddLinkTableNode在链表中插入tLinkTable *类型的数据。InitMenuData在链表中插入的数据类型为tDataNode*,体现了多态,即让父类(tLinkTableNode)指针指向子类(tDataNode)的对象,此时,需要注意的是,要进行强制类型转换。
总结,SearchLinkTableNode这个功能体现的接口设计分层:(代码把数据和业务分离,实现了抽象分层)
- linktableInternal.h定义了基本链表结构
- linktable.h定义链表需要实现的函数,定义了call-in、callback函数的接口
- linktable.c实现链表函数和call-in函数(定义它根据callback函数的情况如何做出反应)
- menu.c使用链表函数,并定义callback函数
作者:095