《编程导论(Java)·9.3.1回调》回调的用途

本文探讨了回调机制在Java编程中的应用,特别是在事件驱动和RMI中的角色。回调作为框架设计的基础,分为提供策略和接收数据两种主要用途。文章通过比较轮询与回调,阐述了回调如何体现好莱坞原则,并在分层环境中实现多态。回调机制避免了双向依赖,保证了代码的清晰性和效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

什么是回调机制(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.轮询与通知

例如上层模块Client调用了下层Server的copy()方法。假设上层需要更新进度条——显示复制任务完成的进度,显然Client并不清楚复制的进度而只有下层的Server才知道。这时下层模块如何将进度数据传递给上层的Client呢?
上层模块获得进度数据的方式:轮询和通知。通知的代码见 回调与Java8的λ表达式
轮询——不停地查询。Server将进度数据保存在一个成员变量x中,并提供getX()。这样Client就可以时时刻刻地查询该数据。如同现实生活中打的士到某地,从上车的第一秒开始,时刻或每隔5秒问司机到了地方没有,也太烦人了。轮询方式下,Client0在一个线程中让Server0努力的复制,主线程则在 while(true)中随时随地的查询数据,直到复制进度数据为100才结束。

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。*/





评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值