您的位置:首页 > 编程语言 > Java开发

Java的回调函数及模板方法

2013-04-20 20:32 169 查看
  刚开始接触回调函数,确实感觉有点玄乎其玄,仔细多看看也就明白了。

  Java的回调函数一般是这样用的:声明一个接口A,接口中定义一个回调方法doSomething(),然后在调用对象B的方法methodB()时,把接口A的一个实现a作为参数传进去,在methodB()方法体中通过a.doSomething()来实现回调。

  一般情况下,我们需要调用哪个方法,就先实例化该方法所在类的一个对象,然后通过该对象取调用目标方法,一切似乎水到渠成,为什么还要用回调函数调来调去呢?在上面的回调过程中,在调用methodB()时,我们完全可以不传参数,在要调用doSomething()的时候再实例化一个接口A的实现。但是,这样做会导致一个问题,methodB()必须知道接口A的具体实现,这样灵活性就会大大降低,比如接口A换了一个实现就必须得改methodB()的代码。

  举个例子:假设一个项目需要由项目经理和程序员两个人来共同完成,项目经理和程序员待定,10天后将项目情况反馈给客户。

  首先,声明一个接口,定义一个回调方法:

package com.kinyoung;
/**
* 声明一个接口,提供回调的方法
* @author Kinyoung
*
*/
public interface Coding {

/**
* 程序员能否在指定时间内完成编码工作
* @param dayRemain 剩余的编码天数
*/
public void code(int dayRemain);
}
  接着,定义项目经理类,项目经理的能力和水平不一样,这里用抽象类:
package com.kinyoung;
/**
* 将项目经理进行抽象
* @author Kinyoung
*/
public abstract class ProjectManager {

/**
* 项目经理进行工作安排,任务步骤是固定的
* @param action
*/
public void assignWork(Coding action) {
int dayToal = 10;
System.out.println("项目计划在10天内完成!");

System.out.println("第一步:系统架构和设计");
int dayDesignCost = design();

System.out.println("第二步:编写代码");
//假设留给程序员写代码的天数依赖于架构设计所用的天数
//此处是通过传入对象来调用code()方法的,实现回调
action.code(dayToal - dayDesignCost);

System.out.println("第三步:反馈客户");
feedback();
}

/**
* 不同项目经理的能力是不一样的,其架构设计的思想及细节是不同的,留给子类去实现
* @return 架构设计所用的天数
*/
protected abstract int design();

/**
* 反馈相关信息给客户
*/
private void feedback() {
System.out.println("完成/未完成任务!");
}
}
  然后,来个具体的项目经理,初级的、高级的、打酱油的都有,我们随便来一个吧:
package com.kinyoung;
/**
* 高级项目经理
* @author Kinyoung
*/
public class SeniorProjectManger extends ProjectManager {

/**
* 高级项目经理的设计能力果然比较强
*/
@Override
protected int design() {
System.out.println("我是高级项目经理,只需要3天时间就能完成设计!");
return 3;
}
}
  按理接下来应该指定个程序员的,但程序员实在太多了,找谁好呢?暂时还没想好,先做个估算,看一下项目能否按时完成吧:
package com.kinyoung;
/**
* 测试一下看能不能按时完成项目任务
* @author Kinyoung
*/
public class Test {

public static void main(String[] args) {
ProjectManager pm = new SeniorProjectManger();
//找哪个程序员来完成编码呢?
pm.assignWork(new Coding(){

//想到要找哪个程序员了
@Override
public void code(int dayRemain) {
System.out.println("只剩下" + dayRemain + "天的时间来完成");

System.out.println("我是菜鸟,需要8天才能写完代码!");
//System.out.println("我是大牛,我只需要5天就能写完代码!");
if (dayRemain < 8) {
System.out.println("结果:不能按时完成!");
} else {
System.out.println("结果:按时完成!");
}
}

});
}
}
  运行结果:
项目计划在10天内完成!
第一步:系统架构和设计
我是高级项目经理,只需要3天时间就能完成设计!
第二步:编写代码
只剩下7天的时间来完成
我是菜鸟,需要8天才能写完代码!
结果:不能按时完成!
第三步:反馈客户
完成/未完成任务!

  在这个例子中,指定程序员编码用了回调。我们看ProjectManager类,项目经理在安排工作assignWork()的时候还没确定程序员,所以传入了一个Coding的接口,可以把这个接口比喻成所有会写代码的程序员名单。等到项目经理完成架构设计后,这时候心中已经有程序员人选了,然后在程序员名单中找到这个人选,即通过接口传入的对象来回调code()方法。

  仔细想一想,我们是否可以让程序员跟项目经理那样用一个个不同的类来做呢?答案是肯定的,但是,如果有100个程序员,我们就得写100个类来描述每个程序员的能力,这样很不合理。而用回调函数,我们只要把100个程序员的名单传过去,程序员的能力如何暂时不管,等到项目经理要用人的时候,拿出名单选一个,再鉴定一下能力就行(这里用匿名类使代码更简洁,也可用内部类)。

  另外,例子中还用到模板方法的思想,ProjectManager其实就是一个模板,它规定了项目的步骤是架构设计、编码实现、反馈客户这个流程,无论是哪个项目经理来负责这个项目都必须按照这个流程来进行,而流程中架构设计师怎样做的、程序员是怎样编码的,对于ProjectManager来说一概不关心,只要子类按照ProjectManager规定的方法来实现就行了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息