一、什么是Callback函数?
当程序跑起来时,一般情况下,应用程序(application program)会时常通过API调用库里所预先备好的函数。但是有些库函数(library function)却要求应用先传给它一个函数,好在合适的时候调用,以完成目标任务。这个被传入的、后又被调用的函数就称为回调函数(callback function)。
回调函数不是实现该函数的软件模块直接调用,而是在特定的事件或条件发生时由另外的软件模块通过函数指针的方式调用,用于对该事件或条件进行响应,是一种下层软件模块调用上层软件模块的特殊方式。
二、Callback函数通过参数进行解耦合的方法
通常有以下三种耦合方法
公共耦合
软件模块之间共享数据区或变量名的软件模块之间即是公共耦合,显然两个软件模块之间的接口定义不是通过显式的调用方式,而是隐式的共享了共享了数据区或变量名。
数据耦合
在软件模块之间仅通过显式的调用传递基本数据类型即为数据耦合。
标记耦合
在软件模块之间仅通过显式的调用传递复杂的数据结构(结构化数据)即为标记耦合,这时数据的结构成为调用双方软件模块隐含的规格约定,因此耦合度要比数据耦合高。但相比公共耦合没有经过显式的调用传递数据的方式耦合度要低。
在lab5.2的代码中
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函数作为callback函数允许一定程度的用户自定义,同时callin函数和callback函数中都添加了一个额外参数args,且为void*类型,保证了模块的通用性,将两个模块一定程度上解耦合。
使用了回调函数机制的模块
tDataNode* FindCmd(tLinkTable * head, char * cmd)
{
return (tDataNode*)SearchLinkTableNode(head,SearchCondition,(void*)cmd);
}
此处SearchCondition作为回调函数接受FindCmd传入进来的参数,该方式通过FindCmd的函数体和SearchLinkTableNode中的额外参数实现,耦合度小于公共耦合。
利用callback函数参数使Linktable的查询接口更加通用,有效地提高了接口的通用性。
我们还通过将linktable.h中不是在接口调用时必须内容转移到linktable.c中,这样可以有效地隐藏软件模块内部的实现细节,为外部调用接口的开发者提供更加简洁的接口信息,同时也减少外部调用接口的开发者有意或无意的破坏软件模块的内部数据。通过接口进行信息隐藏已经成为面向对象编程语言的标准做法,使用public和private来声明属性和方法对于外部调用接口的开发者是否可见。