作业3:深入理解Callback函数

相关文件

在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函数,实现了业务数据与业务处理的解耦。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值