相关文件
在linktableInternal.h文件中定义了LinkTable和LinkTableNode的数据结构。
struct LinkTableNode
{
struct LinkTableNode * pNext;
};
/*
* LinkTable Type
*/
struct LinkTable
{
struct LinkTableNode *pHead;
struct LinkTableNode *pTail;
int SumOfNode;
pthread_mutex_t mutex;
};
作为通用的链表结构,这部分只与链表的逻辑部分有关,与实际的业务部分无关。
在linktable.h中使用typedef为这两个数据结构起了别名,并定义了链表相关的接口,在linktable.c中提供了相关具体实现。
#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_ */
在menu.c中,则定义了与具体业务相关的数据结构tDataNode。
typedef struct DataNode
{
tLinkTableNode head;//必须在首位,指针的强制转换才能成功
char* cmd;
char* desc;
int (*handler)();
} tDataNode;
接口设计中的抽象分层
在DataNode中,排在最前面的是tLinkTableNode类型数据,如果想将tDataNode指针转变成tLinkNode指针,tLinkNode这一项必须在结构体的最前面。在C语言中,32位计算机的所有类型的指针大小均为4字节(64位是8字节),指针中存放的是所指数据的地址。假设
DataNode a;
DataNode* p1 = &a;
tLinkTableNode* p2 = (tLinkTableNode*) p1;
那么p1和p2的值相同,即指向同一地址,只不过p1以DataNode结构体的形式解析,p2以tLinkTableNode的形式解析,这样p2只能访问前sizeof(tLinkTableNode)字节内容,DataNode的其余部分对p2隐藏,而p1可以全部访问。所以tLinkNode这一项必须在DataNode结构体的最前面。
/* menu.c */
int InitMenuData(tLinkTable ** ppLinktable)
{
*ppLinktable = CreateLinkTable();
tDataNode* pNode = (tDataNode*)malloc(sizeof(tDataNode));
pNode->cmd = "help";
pNode->desc = "Menu List:";
pNode->handler = Help;
AddLinkTableNode(*ppLinktable, (tLinkTableNode *)pNode);//强制类型转化,多态的思想
pNode = (tDataNode*)malloc(sizeof(tDataNode));
pNode->cmd = "version";
pNode->desc = "Menu Program V1.0";
pNode->handler = NULL;
AddLinkTableNode(*ppLinktable, (tLinkTableNode *)pNode);//强制类型转化,多态的思想
pNode = (tDataNode*)malloc(sizeof(tDataNode));
pNode->cmd = "quit";
pNode->desc = "Quit from Menu Program V1.0";
pNode->handler = Quit;
AddLinkTableNode(*ppLinktable, (tLinkTableNode *)pNode);//强制类型转化,多态的思想
return 0;
}
/* linktable.c */
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;
}
对于多态的形式,以InitMenuData和AddLinkTableNode为例,AddLinkTableNode在linktable.c中,属于链表的逻辑结构部分,而InitMenuData与具体的业务部分有关,在menu.c中。现在业务部分只需关心具体的业务数据,而不必在意逻辑部分的细节,只需在创建完成业务节点后使用AddLinkTableNode(*ppLinktable, (tLinkTableNode *)pNode);将pNode强制转换为tLinkTableNode类型即可,通过多态的方式,实现了逻辑部分和业务部分的解耦。
Callback函数
typedef struct DataNode
{
tLinkTableNode head;
char* cmd;
char* desc;
int (*handler)();//函数指针
} tDataNode;
在tDataNode中,int (*handler)()为函数指针,作为指令的具体处理,使用callback方式调用函数。
int InitMenuData(tLinkTable ** ppLinktable)
{
*ppLinktable = CreateLinkTable();
tDataNode* pNode = (tDataNode*)malloc(sizeof(tDataNode));
pNode->cmd = "help";
pNode->desc = "Menu List:";
pNode->handler = Help; //节点与处理函数关联
AddLinkTableNode(*ppLinktable, (tLinkTableNode *)pNode);
pNode = (tDataNode*)malloc(sizeof(tDataNode));
pNode->cmd = "version";
pNode->desc = "Menu Program V1.0";
pNode->handler = NULL; //节点与处理函数关联
AddLinkTableNode(*ppLinktable, (tLinkTableNode *)pNode);
pNode = (tDataNode*)malloc(sizeof(tDataNode));
pNode->cmd = "quit";
pNode->desc = "Quit from Menu Program V1.0";
pNode->handler = Quit; //节点与处理函数关联
AddLinkTableNode(*ppLinktable, (tLinkTableNode *)pNode);
return 0;
}
在InitMenuData中,定义节点时,使用pNode->handler = 函数地址 的方式将节点与处理函数想关联。
int main()
{
InitMenuData(&head);
/* 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();//callback方式调用函数
}
}
}
在main中我们使用p->handler()即callback方式来调用函数。这样无需关心业务的数据,只需关心业务的处理,并且当产生新的指令需求时,只需要在InitMenuData中添加新的节点即可,而无需改变main函数,实现了业务数据与业务处理的解耦。