1.1 回调机制(Call back)

返回目录

要点:

回调(call back,一个动词词组)机制,是一门编程语言,使得下层模块/库可以调用或执行上层模块定义的代码的机制。上层模块所定义的、被(下层模块)调用或动态绑定的代码,则被称为回调函数 (简称回调、callback,一个名词)。

框架是骨架式方案,需要上层模块(为该骨架式方案)提供代码支持。

Java程序员从C语言的回调机制/Call back是“回过头来调用”,会陷入望文生义的泥潭,并从“你调用我我回过头来调用”的暗示中,将回调函数看成通知机制(参见[1.3好莱坞法则])的专用品


在软件设计中,分层设计(Layered designs)是软件开发时常用的策略,例如管理信息系统常用的逻辑三层结构——表现层、业务逻辑层(business logic layer)和数据访问层(data access layer)。为了讨论方便,可以简单地将代码分为两层,如Java程序开发可以粗略地分为两层:应用程序称为上层模块,而被应用程序依赖的库,称为下层模块/基础设施,如JDK(和第三方包)。

1.1.1什么是回调机制

在分层架构中,存在一条显而易见的军规:

★上层模块依赖于下层模块。下层模块不可能依赖于上层模块。

例如JDK的设计者在设计下层模块/库时,不可能知道应用程序中使用什么类名、函数名等等,也就是说,下层模块对上层模块一无所知。(对于分层,本书点到为止。软件组织的探讨,更加抽象)。在分层架构有这一严格军规的情况下,有一个值得研究的有趣机制——回调机制。既然下层模块不可能依赖于上层模块、下层模块对上层模块一无所知,那么下层模块为什么要、以及怎样调用上层模块的函数呢?这就是回调机制要探讨的问题,以及回调机制魅力所在。

回调机制有两种使用场景:

  • 框架设计中。下层模块中的框架,需要上层的应用程序提供具体的策略。[1.1.3回调机制的实现]将通过一个例子的多种编程语言的实现,说明各种编程语言如何实现回调机制,它将涉及各种编程语言的核心概念。[1.2框架设计者]将进一步展开讨论框架问题。
  • 通知机制中。下层模块运行的时候,通过调用上层模块/应用程序的函数,将下层所知道的数据,通过实参传递给应用程序处理。[1.3通知机制]介绍这方面的内容。

因此,回调机制是各种编程语言需要提供的常用处理方式,虽然不同语言采用了不同的技术和术语。

回调机制(call back,一个动词词组),是一门编程语言,使得下层模块/库可以调用或执行上层模块定义的代码的机制。上层模块所定义的、被(下层模块)调用或动态绑定的代码,则被称为回调函数 (简称回调、callback,一个名词)。

1.为什么将回调机制置于分层架构中

请注意,本章所讨论的问题,很多时候可以用于非分层的场合。这里,之所以提及和强调分层,并非技术上的需要,而是理清思路的需要。因为在分层条件下,程序员需要考虑的问题清晰明了——怎样完成回调。强调分层,类似极限生存训练,一个人在极限条件下能够生存,他在正常条件下的存活就不在话下。有些人非分层的场合讨论回调机制,将回调机制讨论得不知所云。

2.回调机制的要点

回调机制不单纯地用于通知机制中,也不单纯地用于框架设计中。回调机制有两种使用场景,从代码的结构图上看通常一样。将两者区别开来,才能够正确理解回调机制为什么是一种目的性机制——下层模块找到上层的代码,而非技术性机制。回调机制涉及模块之间的调用,但是它与同步调用和异步调用不可等量齐观。例如通知机制中,其回调既可以设计成同步的,也可以是异步的。

回调机制通常不是讨论模块A与模块B两者之间的调用关系,而是上层模块与下层模块之间的调用关系。模块A调用模块B,模块B反过来调用模块A,而且AB在同一层中,这种回调机制的使用场景,没有任何意义。

1.1.2 框架Vs. 工具箱

回调机制的第一个使用场景是框架。

什么是框架(/framework)?作为OOD学习的出发点之一,这里需要给框架一个十分简化的定义。下层(通常说底层)模块,常常被称为库/ Library ( 简写 Lib ),库中包括工具箱与框架。因此,框架是与工具箱相比较而存在的概念

工具箱是库中预定义的功能模块。最简单的工具箱,常常被称为库函数,如java.lang .Math,它封装如函数abs、sin、max、random等,形成一个数学工具集;再如C语言的函数库;一般所指的工具箱,相对框架而言,它表现为一个完整的解决方案。如javaI/O流、Java集合类库、Spring依赖注入容器等。

框架是为了某种应用类型中面临的共同的细节或流程,而在下层模块/库中预定义的一个骨架式方案。在事件驱动程序/GUI编程、Java远程方法调用(Remote Method Invocation, RMI),JUnit等框架中,框架封装了特定应用类型的控制逻辑,能够以标准的方式来构建并部署应用。因此,

★框架是骨架式方案,需要上层模块(为该骨架式方案)提供代码支持。

在事件驱动程序/GUI编程、Java远程方法调用(Remote Method Invocation, RMI),applet等框架中,框架封装了特定应用类型的控制逻辑,能够以标准的方式来构建并部署应用。要点在于框架仅仅是骨架,应用程序员在产生特定应用的软件、按照自己面临的需求而使用框架时,要给出自己的一些功能实现代码——按照框架填入自己的东西。形象地说,应用程序员使用框架时要填空式编程。例如GUI编程,程序员有两方面的工作,他需要按照实际的需求使用各种窗口、菜单、按钮和各种控件,得到自己需要的界面布局,而界面布局这方面的编程工作,其实与使用Math工具没有差别;另一方面,框架的设计者不可能预知用户点击一个按钮时,应用程序应该如何响应,而界面的响应代码/功能实现代码,需要应用程序员给出,此时,应用程序员必须按照框架的约束来填入自己的东西。

工具箱与框架的区别在于:工具箱是单纯的被调方,如应用程序调用java.lang .Math;基于框架的应用程序,可能会调用框架,而框架运作过程中,框架是调用方,它通过回调机制,(C语言)调用或(Java语言)动态绑定上层模块提供的支持代码。

对于工具箱与框架的区别,业界使用了各种术语来形容,如控制反转、好莱坞法则等,这些术语对于学习OOD并没有任何好处。在后面的章节还是会讨论到它们,不是因为它们重要,而是因为它们经常出现在一些书籍和文章中,像苍蝇一样,理它吧,劳神费力;不理它吧,嗡嗡的讨厌。

★判断所使用的库是否框架的唯一标准,是程序员在使用它时(在Java中)是否编写了@Override方法。

下面将举一个例子,使读者对框架、回调机制等概念有一些感性认识;然后再介绍该例子背后蕴涵的道理。

1.1.3 回调机制的实现

注:本节单独成文,增加操作步骤的介绍,归于[实验]栏目。

1.1.4 什么是回调(函数)?

上层模块所提供的支持代码,是否需要一个术语——例如回调函数(callback)来描叙呢?

Scheme语言中,因为高阶函数的存在,连回调机制这个术语都不必要,更不需要为(应用程序调用框架时,能够)被作为实际参数的函数,设定一个特别的名词;

C语言中,回调机制/Call back很形象。但是,只要符合函数指针指定的函数种类的所有上层函数,都是“潜在的”支持代码。实践中很难将“被作为参数的函数”和“能够作为参数的函数”加以区分。

Java中op函数不需要第三个参数,即不需要把回调函数作为参数。Java的回调函数与一般函数有着明显的目的上的区别。回调函数是上层模块为下层提供的支持代码,是为框架准备的。

★ (Java) 上层模块中编写的、上层模块不会自己去调用的函数,即回调函数。

典型的回调函数例子,Java多线程中run()、GUI中各种事件处理函数如actionPerformed(),虽然程序员编写了run()的代码,但是从来没有自己调用它。严谨起见,例程1-3中,下层模块/框架BinaryOP中定义的抽象方法op,称为回调接口;上层模块,BinaryOP的实现类给出的@Override修饰的op称为回调函数。

综合上述情况,就引出了一个有意思的问题:人们定义一个(如回调函数的)概念时,能否通用于各种编程语言中?如果不能通用,该概念将导致混乱。

例如维基百科给出如下定义:回调函数指可以被作为参数传递给其他代码的可执行代码块,或者一个可执行代码的引用

这种C语言视角的定义,能够解释C语言实现中的回调函数,如

int op(int a, int b, How_op  how_op)

其中op将调用的实参如plus是一个回调函数,即回调函数是通过函数指针调用的函数。但是,该定义在Java的实现中不适用,因为Java的回调函数并不需要作为某个(高阶函数的)参数;这种C语言视角的定义,在Scheme语言中,“其他代码”被称为高阶函数,如果说回调函数是被高阶函数作为参数的函数,显然Scheme不需要这种回调函数的概念。

如果Java程序员从维基百科的定义出发去理解回调函数,会混淆第二章行为参数化的讨论议题;如果Java程序员从C语言的回调机制/Call back是“回过头来调用”出发,会陷入望文生义的泥潭,并从“你调用我我回过头来调用”的暗示中,将回调函数看成通知机制的专用品。

1.1.4匿名类和λ表达式

Java提供支持代码(编写回调函数)的更多方式


探讨

1.什么是回调(函数)?

可以把C语言中Plus 不纠结地称为回调函数,甚至你把 op(int a, int b, How_op  how_op)称为回调函数都可以,反正你真正需要知道的只是函数指针,总之:C语言用函数指针实现回调机制(Call back),下层调用函数指针所指向的函数。这样,回调(函数)就失去了讨论的价值,把天聊死了。

2.应用程序可以调用回调函数吗?在C语言中,如果把Plus称为回调函数,很难阻止程序员调用回调函数(Java中也可以调用回调函数。但是Java中回调函数与一般函数的区别比较明显)

3.更一般地,回调函数可以理解为:在设计框架时使用高阶函数

  • 没有高阶函数,那么各种语言就需要给出自己的替代品。
  • 高阶函数是单纯的编程语言的概念,不涉及程序组织的分层概念;而回调,需要这个分层概念。因此回调是在设计框架时使用高阶函数。技术上,与在非分层场合时的一般使用,没有什么区别。

从提供回调函数的方法体的角度看,λ表达式最为简洁。因此,Java 8你肯定要学习,大家都会马上使用它。

4.望文生义问题,我们在学习[1.3通知机制]后,再去看看网上的一些什么是回调的文章,现在不要看,免得被灌得一头浆糊。

【在具有高阶函数的语言中,回调(函数)基本上是一个不需要存在的概念。比如说你天天吃肉(app设计时经常使用高阶函数),你会为过年吃肉(在设计框架时使用高阶函数)搞出一个名词加餐(回调函数)吗?】

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值