在什么是回调机制(Call back)中,介绍了回调机制/call back、回调函数/回调方法(简称回调、callback)的概念。
回调机制在事件驱动程序/GUI编程、Java远程方法调用(Remote MethodInvocation, RMI)等领域被广泛采用,applet的生命周期、绘制方法都是回调。回调机制是理解委托事件模型的基础。更一般地说,回调是框架设计的基本手段。
回顾例程9-1
package API..graphics;
public class FirstApplet extends java.applet.Applet{
@Override public void init(){
//你的代码
}
@Override public void paint(Graphics g){
g.setColor(Color.red); //告诉g变换颜色
g.drawRect(100,100,100,100);//draw a rectangle (xco,yco,x-width,y-height);
g.fillRect(110,110,80,80); // 填充/fill一个小rectangle
}
}
其中,@Override标记的方法 init()和paint(Graphics g)都是回调。
编写框架/类库(或者操作系统如Windows)时,上层模块要提供代码。上层模块什么情况下要给下层提供代码?回调的应用场景是什么?
- 上层模块提供操作策略。框架需要上层模块的回调函数提供代码,接口util.DoubleOP的op(double m,double n)函数,对两个double操作后返回一个double值,但是进行什么操作呢,相加,比较、求幂?应用程序App可以根据自己的情况提供回调函数。
- 上层模块获得数据。下层模块(如工具类、框架)不得直接调用上层中某个类的方法/接口(因为JDK完全不知道应用程序中有什么类,更不知道它的方法/接口)。然而在编程实践中,下层模块常常希望反过来调用上层的方法(以便向上层模块传递数据)。
1.轮询与通知
package API.event.lower;//意味底层模块所在的包
public class Server0{
private int x;//复制工作的进度
public int getX(){ return x; }
public void copy() {
while(x<100){
try{
Thread.sleep(10);
x++;
}catch(InterruptedException e){}
}
}
}
package API.event;//意味上层模块所在的包
import API.event.lower.Server0;
/**
* 补充:上层模块获得进度数据的方式:轮询和通知。
* 这里演示轮询
*/
public class Client0{
private Server0 server =new Server0();
public void call() { server.copy(); }
public static void test() throws InterruptedException{
Client0 upper =new Client0();
Runnable r = ()->upper.call();
new Thread(r).start();
System.out.print("进度:");
int x2 = 0;
while(true){
int x1 = upper.server.getX();///10
Thread.sleep((int)(Math.random()*100));
if(x2 >=100){
System.out.println();
break;
}
if (x2 != x1) {
System.out.print(x1+"% ");
x2 = x1;
}
}
}
}
下面是两次测试的结果。
进度:3% 5% 9% 16% 21%23% 29% 31% 38% 48% 57% 63% 70% 76% 82% 85% 87% 88% 90% 91% 97% 100%
进度:1% 6% 12% 16% 24%31% 39% 47% 49% 54% 61% 64% 67% 77% 83% 90% 100%
轮询方式的优点:- 上层模块能够随时随地的了解所需的数据(不会被下级欺瞒),依赖关系和方法调用关系都很明确;
- 上层模块获得的数据是实时数据,如9%、72%,而不是10%、20%这种满足通知条件的数据。
- 下层模块代码简洁。
- 上级时刻盯着下级干活,比较讨嫌;
- 获得数据时执行太多的调用(特别是在网络编程如远程方法调用时,这是不能够容忍的);
2.提供策略 Vs. 获得数据
回调机制(call back)是分层架构中的多态。但是在通过回调(而非轮询)获得数据时,应用了好莱坞原则。
所以:
Q:好莱坞原则=通知 ?
A:在不允许双向依赖(你call我,我call 你——没有back!)时,好莱坞原则=通知,与轮询对立。或者说,通知=观察者模式=好莱坞原则
Q:提供策略的回调与通知/观察者模式/好莱坞原则一点关系都没有?
A:是啊。
Q:接收数据的回调 = 好莱坞原则?
A:嗯
代码中,获得数据,上层模块需要将自己的引用传递给下层模块,或者调用下层模块的注册方法。
3.回调机制(call back)是分层环境中的多态
如果忽略分层,回调与通常的多态,从类图和其自身代码上,没有区别。
/*回调机制(call back)是分层架构中的多态。两者没有关系。
回调有两个基本用途:
- 提供代码。框架需要上层模块的回调函数提供代码。
- 接收数据。框架向上层模块的回调函数传递数据。
接收数据时,回调机制应用了好莱坞原则。
另一方面,在非分层场合,通知可以简单地call来实现(造成双向依赖和你call我,我call 你——不是call back!),而不一定需要观察者模式/好莱坞原则。只有在分层场合,上层接收数据又不采用轮询,必须使用回调机制和必须使用好莱坞原则。
在什么是好莱坞原则中,yqj2065大话连篇,木有代码。这里补充一点代码。
call:在一般的场合,Client的代码中(call)调用IServer的方法;
callback:如果IServer属于框架,IServer需要向上层模块Client传递数据或需要上层模块提供代码,但是又不知道上层模块的任何情况(IServer怎么知道你为应用程序的类取的名字咧),不过,IServer聪明地调用IClient的方法foo(),而上层模块Client或叫App等等名字,继承IClient并@Override foo()。
效果上看,IServer call back client。
注意:call back不是说你call我,我call back 你,这是打乒乓球。call back指框架调用了(事实上是动态绑定后执行了)上层模块的代码。
因此,call和call back都是指函数的调用,非分层中称为call;分层中,上层模块调用库函数也称为call,而框架调用上层模块的代码称为call back。*/