简介
输入只能是一个变量。也可以是一个执行逻辑。
2018.6.28补充:现在看,回调基本可以视为函数式编程中 函数的概念。以高阶函数来看待将回调作为参数的函数。
而“输入可以是一个执行逻辑”的意义远比我们想象的大。
回调在编程中的应用
替换模板模式
我们先来用模板模式实现一个功能,下面的B类可以看成是现实中的HibernateTemplate类
public abstract class B{
public void execute(){
getConnection();
doCRUD();
releaseConnection();
}
public abstract void doCRUD();
public void getConnection(){
System.out.println("获得连接...");
}
public void releaseConnection(){
System.out.println("释放连接...");
}
}
public class A extends B{
public void doCRUD(){
add()
}
public void add(){
...
}
}
public class C extends B{
public void doCRUD(){
delete()
}
public void delete(){
...
}
}
用回调的办法实现下
interface CallBack{
public void doCRUD();
}
class A{
private B b;
public void add(){
b.execute(new CustomCallBack(){
public void doCRUD(){
System.out.println("执行add操作...");
}
});
}
}
public class B{
public void execute(CallBack action){
getConnection();
action.doCRUD();
releaseConnection();
}
public void getConnection(){
System.out.println("获得连接...");
}
public void releaseConnection(){
System.out.println("释放连接...");
}
}
可以看到,使用回调后,AB从模板模式中的父子关系,变成了依赖关系。模板模式和回调都可以实现一种效果:在一个完整的过程中预留接口,交给其它类实现。
在分层中的应用
比如netty,一旦一个netty server开始运行,它就是一个完整的运行逻辑,有自己的线程模型。笔者以前做的一个抽象,将message处理代码从handler里抽取出来,自己定义一个messageDispatcher,messageDispatcher根据相关字段分发到不同的processor。其实我可以再进一步,将dispatcher和processor完全抽取出来,封装成一个messagehandler,传给netty server。
这样,netty server就完全可以独立作为一个数据传输层而存在。messagehandler单独作为一个数据协议层。从代码的运行情况看,数据协议层和数据传输层不分家。但从代码的管理角度,它们可以是两个项目,从设计上这样做的优越性就更大了,只要定义好数据协议层和传输层的接口,具体实现可以随意替换。
回调在拆分中的作用
假设原来有一个比较复杂的方法,想将其拆分成两个方法(此场景也适合拆分类)。假设我们要抽出的步骤是不连续的,比如将235抽取成一个函数),那么就要用到回调。(在实际的应用中,会出现“步骤14使用了同样的数据,并且从语义上它们更适合在一起”的情况)
class A{
void func(){
1.xx
2.xx
3.xx
4.xx
5.xx
}
}
class A{
void func(){
1.xx
func2(arg,new Callback(){
void callback(){
4. xx
}
})
}
void func2(arg,callback){
2. xx
3. xx
4. callback()
5. xx
}
}
在两个线程间使用callback
map<long,callback> callbackManager
sendThead{
void send(Message,callback){
callbackManager.put(message.getId(), callback);
ioBusiness.send(message);
}
}
receiveThread{
void receive(message){
// 收发的同一个message必须具备相同的id
callback = callbackManager.get(message.getId());
callback.run();
}
}
跟线程间callback差不多,只是需要一个callbackManager来维护映射关系。
回调在架构设计中的应用
http callback
最近一段学习marathon,发现一个有意思的东西。我们可以向marathon发出订阅请求,当发生某个事件时,marathon主动向外界发出http请求,我们写一个http服务监听,即可感知到某个事件的发生。