今天用到回调,也看了些网上的文章,这里分享一下个人的心得体会
回调,字面意思是返回的调用,是相对于“调用”而言的。A调用B,B的执行过程中又反过来调用A,这就是回调。
以下约定,其中A称为客户端,B称为服务端,以便于描述。
回调是一种设计思想,“回调就是对函数指针的一种运用方式”这只能说是语法层面的理解,
还没有达到设计层面的高度。设计模式中,访问者模式(Visitor Pattern)中的两个要素之间就存在回调关系。
也就是说通过服务端B封装一些逻辑过程,但其中某个环节又需要暴露给客户端A,以提供自由度。
回调的范式1:
{
typedef int (__stdcall *CallbackFunc)(LPARAM P1, LPARAM P2, LPARAM P3);
int ServerFunc(LPARAM P1, CallbackFunc fn, LPARAM P3);
}
ServerFunc是主调函数,是服务端B提供给客户端A的接口,由客户端A提出规格要求。
CallbackFunc是回调函数,是客户端A提供给服务端B的回调接口,由服务端B提出规格要求。
P1是A传给B的参数,P2是B传给A的参数,P3是A发起的需要传入回调函数的参数。
这里参数P3是一个奇怪的存在,因为P3对于B没有任何意义,但是却不得不在B的接口中体现,
设计上可以称之为“对B的接口打洞”。实际应用中还确实有这种需要。那么如何解决这个问题呢?
还记得标准库中的仿函数算子(Functor)么,譬如std::less,也是一种回调的实现方式,其原理是,
实现一个类(或结构体),重载圆括号运算符,即相当于回调函数。想想使用类有什么好处呢?
对了,类是可以有成员变量滴,哦耶,这下我们可以把参数P3做为Functor类的成员变量了,如果
需要增加P3类参数,也不再影响服务端B了,very ok。
回调的范式2:
{
// 服务端B
struct ICallbackFunctor
{
virtual int operator()(LPARAM P1, LPARAM P2)=0;
};
int ServerFunc(LPARAM P1, ICallbackFunctor fn);
// 客户端A
struct CallbackFunctor : public ICallbackFunctor
{
virtual int operator()(LPARAM P1, LPARAM P2)
{
}
LPARAM P3;
};
}
再说说泛型化,标准库是用模版来实现泛型的,其实使用多态也是可以的,别说想不到,
重载运算符也是可以声明为virtual的,所以完全是可以实现多态的算子的,这样扩展起来更方便了。