一个通俗的举例:一个人到一个商店买东西,刚好要的东西没有货,于是在店员那里留下了电话,过了几天店里有货了,店员就打了他的电话,然后他接到电话后就到店里去取了货。在这个例子里,他的电话号码就叫回调函数,他把电话留给店员就叫登记回调函数,店里后来有货了叫做触发了回调关联的事件,店员给他打电话叫做调用回调函数,他到店里去取货叫做响应回调事件。
回调函数其实就是一个通过函数指针调用的函数!
假如你把 A 函数的指针当作参数传给 B
函数,然后在 B
函数中通过 A
函数传进来的这个指针调用 A
函数,那么这就是回调机制。A
函数就是回调函数,而通常情况下,A
函数是系统在符合你设定条件的情况下会自动执行。
在此项目中menu.c引用linktable.h,linktable.h引用linktableInternal.h,linktable.c引用linktable.h。
linktableInternal.h:linkTable结构体是链表,包含链表头、尾结点,以及一个int型存储链表中节点值之和,还有一个互斥变量mutex;文件里也定义了链表结点结构体LinkTableNode,这里只声明要有最基本的属性即指向下一个LinkTableNode的指针pNext。
linktable.h:由linkTable和LinkTableNode定义新结构体tLinkTableNode和tLinkTable,这个文件里基于这两个新结构体规定了要实现的链表应该实现哪些方法,包括删除链表、添加结点、删除结点、查抄结点、获取头、尾结点。如果希望链表实现另一套方法,可以仿照linktable.h包含linktableInternal.h,写另一套接口。
Callback函数需要一个Call-in函数,接口定义如下
/*
* Search a LinkTableNode from LinkTable
* int Conditon(tLinkTableNode * pNode, void * args);
*/
tLinkTableNode * SearchLinkTableNode(tLinkTable *pLinkTable, int Conditon(tLinkTableNode * pNode, void * args), void * args);
linktable.c中Condition允许用户自定义,通过这个办法实现了用户自定义查询条件,以在链表中查询结点的功能
menu.c中定义了查询条件
在findCmd函数中用到了这个“在链表中按照给定查询条件查询结点”的功能
FindCmd调用SearchCondition,把自己的参数传给后者。
公共耦合
当软件模块之间共享数据区或变量名的软件模块之间即是公共耦合,显然两个软件模块之间的接口定义不是通过显式的调用方式,而是隐式的共享了共享了数据区或变量名。
数据耦合在软件模块之间仅通过显式的调用传递基本数据类型即为数据耦合。
标记耦合在软件模块之间仅通过显式的调用传递复杂的数据结构(结构化数据)即为标记耦合,这时数据的结构成为调用双方软件模块隐含的规格约定,因此耦合度要比数据耦合高。但相比公共耦合没有经过显式的调用传递数据的方式耦合度要低。
根据以上定义,①不给SearchLinkTableNode及其接口Condition加参数,声明一个全局变量,用时赋值;②增加参数,在FindCmd函数里把cmd链表指针传给SearchLinkTableNode。
①属于公共耦合;②属于标记耦合
menu.c中的主函数:先是InitMenuData(&head)初始化了head,再在FindCmd中传递head参数:
head是通过CreateLinkTable创建了一个tLinkTable*即链表头指针,再调用AddLinkTableNode往链表中插入tLinkTable *类型的数据,而InitMenuData函数在这个链表中插入的都是tDataNode*,这里是多态的实现,让“父类”(父结构体tLinkTableNode)指针指向“子类”(子结构体tDataNode)的对象,由于类型不匹配所以要进行强制类型转换。