【关键词】 活与死 回调函数 面向对象设计
【活与死】
在面向过程的程序设计里,main函数是活的,函数库等是死的。可以说,所有非main函数的函数,都是直接或间接被main函数调用。
在MFC程序设计里,所有我们写的处理windows消息的函数,都是MFC这座大楼里(整体框架中)已经设计好的执行单元,我们可以客户化它,但我们不知道这些执行单元什么时候、以及怎样被MFC框架调用——假如我们没有足够理解MFC的话。
我们可以说,main函数是活的,MFC框架是活的;非main函数、我们自定义的消息处理函数都是死的。
可以广义化,调用者是活的,被调用者是死的。调用者与被调用者是相对的,同样,活与死也是相对的。
【什么是回调?】
假如我们能看到main函数的代码,能够了解到我们的函数是在main函数的哪个时机、地点、怎样被调用的,则我们自己写的非main函数是非回调函数。——我们可以改变调用我们函数的调用者,改变调用我们函数的时机。
假如我们根本看不到main函数的代码,不能够了解(或者即使我们能看到看懂其源代码,但没有时机去改变main函数的执行序列),则我们自己写的被main函数调用的函数可称之为回调函数。
对于非回调函数,我们可以自由的改变其形式与结构、改变其含义,如能增加或减少参数、能调整返回值类型——因为我们可以修改调用者(这里广义的称之为main函数)调用它们的方式。
对于回调函数,我们只能按照别人设计的形式与结构、意图与含义去实现,如不能增加或减少参数、也不能调整返回值类型——因为我们不可以修改调用者,无法改变其形式以及要达成的目标。
Windows中定义的回调函数指的是程序员写的供操作系统调用的函数(windows要求必须是静态函数,函数声明必须有callback字样)。
在c/c++那里,回调指的是,某一个结构体/或类的成员其实是一个函数的指针,这个函数指针成员可以根据编程者的需要指向不同执行实体(真正的被执行单元,但这些被执行单元具有相同的函数声明与结构),用以将调用者与被调用者分离,推迟执行时机——你在A处/时给这个函数指针成员指定具体被执行实体,但事实上,这个被执行实体是在B处/时被真正执行。
在.NET中,有与观察者模式异曲同工的事件委托机制(eventHandler),被观察者有一个eventHandler,在客户端给这个eventHandler添加一些列具有相同结构的函数,然后在通知函数里逐个调用这些eventHandler函数——本质上就是回调。
【把“活与死”和“回调函数”放到一起来看】
当被调用者可以要求调用者去适应它(指被调用者)时,被调用者是非回调函数。
当调用者不可以要求调用者去适应它(指被调用者)时,被调用者是回调函数——调用者说你应该XXX、只能yyy,不允许zzz,被调用者必须一一照办。
在面向对象程序设计里,典型的,如:
(1) 在观察者模式里,观察者有一个统一的update函数供被观察者调用。那么,这个update函数就是一个回调函数。
(2) 在模板方法模式里,模板方法与模板方法所调用的那些虚函数,虚函数就是回调函数。
推而衍之,我们可以说:
(1) 实现某个接口的函数,都是回调函数。
(2) 没有统一规则,可以自由设计与变化的函数,都是非回调函数。
你有基于接口设计与编程的意识么?你有被回调的意识么?
【框架与回调】
所有的框架,其执行机制就是回调。——框架就是一个放大了复杂化了的模板方法模式。
没有回调存在的框架就是一个函数库。
框架,让被调用者适应调用者。被调用者根据实际场景,客户化框架中的回调函数(广义的回调函数,如函数、实现类、模块包、客户化组件)。
函数库,让调用者适应被调用者。
所有框架,不管是代码框架、应用框架、业务框架,都是被调用者适应调用者。