回调函数是什么鬼, 回调函数干嘛用,回调函数可以怎么用
如果有过android开发经验,经常可以看到一些类似下面的代码
Button Btn1 = (Button)findViewById(R.id.button1);//获取按钮资源
Btn1.setOnClickListener(new Button.OnClickListener(){//创建监听
public void onClick(View v) {
String strTmp = "点击Button01";
Ev1.setText(strTmp);
}
});
上面注册的监听器其实就包含了这个回调的意味了。
软件模块之间总是存在着一定的接口,从调用方式上,可以把他们分为三类:同步调用、回调和异步调用。 同步调用:一种阻塞式调用,调用方要等待对方执行完毕才返回,它是一种单向调用; 回调:一种双向调用模式,也就是说,被调用方在接口被调用时也会调用对方的接口; 异步调用:一种类似消息或事件的机制,不过它的调用方向刚好相反,接口的服务在收到某种讯息或发生某种事件时,会主动通知客户方(即调用客户方的接口)。
回调和异步调用的关系非常紧密:使用回调来实现异步消息的注册,通过异步调用来实现消息的通知
所谓回调,就是客户程序CLIENT调用服务程序SERVER中的某个函数SA(),然后SERVER又在某个时候反过来调用CLIENT中的某个函数mycallback(),对于CLIENT来说,这个mycallback便叫做回调函数。例如Win32下的窗口过程函数就是一个典型的回调函数。
简单来说,就是在调用一个组建的方法时,按照他的定义,注册一个我们自己的方法,期待这个组建在某一个特地场景下调用我们注册的方法,实现对应的功能
上面简单的说明了什么是回调函数,那么怎么去设计一个回调函数呢?
首先可以明确的是调用方要实现一个注册方法,被调用方提供一个功能,在某些特定的情况下,会调用注册方法
如一个应用场景是:
如查询一条微博的点赞数,在db中保存了用户+微博的点赞映射关系,通常需要获取点赞数的时候,需要在db中count一下,计算点赞数,当点赞数很多的时候,改怎么办?每次都count一把? 性能开销难以接受
一个简单的方法是使用缓存,将点赞数保存在缓存中,每次获取点赞数都从缓存取,缓存没有命中的时候,才从db中count一把,并回写到缓存中
上面这个应用场景该如何设计成回调函数的形式呢?
一般为了通用性而言,CacheClient内部如果将缓存未命中查db的功能代码封装起来,会有什么问题?耦合太高,没法复用
so 形式话的结构如下:
CacheClient:
使用方 CountService:
看上面的描述能看懂么?卧槽,自己写的东西自己都看不大懂啊,果然还是代码是王道,先看看代码,看一下是怎么玩的,然后在回过头去看一下上面的,效果会好很多
注册器相关类:
回调接口 CacheCallBackInterface
package com.mushroom.hui.common.register.callback;
/**
* 缓存未命中的回调函数
* Created by yihui on 16/4/5.
*/
public interface CacheCallBackInterface {
String getKey(int id);
int getExpire();
Object getObject(String key);
}
注册接口 BaseRegister.java --> 为什么要设计成接口?看CacheCient的时候会理解一点的
package com.mushroom.hui.common.register;
import com.mushroom.hui.common.register.callback.CacheCallBackInterface;
import java.util.HashMap;
import java.util.Map;
/**
* Created by yihui on 16/4/5.
*/
public interface BaseRegister {
Map<String, CacheCallBackInterface> map = new HashMap<>();
void register(String name, CacheCallBackInterface callback) throws Exception;
Object exec(String name, int id) throws Exception;
<T> T exec(String name, int id, Class<T> clz) throws Exception;
}
CacheClient.java 对外提供的缓存客户端, 这个里面就实现了传说中的回调函数的使用
package com.mushroom.hui.common.cache;
import com.mushroom.hui.common.cache.api.CacheInterface;
import com.mushroom.hui.common.register.BaseRegister;
import com.mushroom.hui.common.register.callback.CacheCallBackInterface;
import com.mushroom.hui.common.register.exception.RegisterException;
import org.apache.commons.lang.StringUtils;
import javax.annotation.Resource;
/**
* Created by yihui on 16/4/5.
*/
public class CacheClient implements BaseRegister {
private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(CacheClient.class);
@Resource(name = "cacheService")
private CacheInterface methodCache;
@Override
public void register(String name, CacheCallBackInterface callback) throws Exception {
if(StringUtils.isBlank(name)) {
throw new IllegalArgumentException("key is empty!");
}
if (map.containsKey(name)) {
throw new RegisterException("this callback interface has already registered! name : " + name);
}
map.put(name, callback);
}
@Override
public Object exec(String name, int id) throws Exception {
return exec(name, id, Object.class);
}
@Override
public <T> T exec(String name, int id, Class<T> clz) throws Exception {
CacheCallBackInterface callBackInterface = map.get(name);
if (callBackInterface == null) {
throw new RegisterException("this callback interface has already registered! name : " + name);
}
String key = callBackInterface.getKey(id);
Object obj = methodCache.getObject(key, clz);
if (obj == null) {
obj = callBackInterface.getObject(key);
methodCache.setObject(key, obj, callBackInterface.getExpire());
}
return (T) obj;
}
/**
* 获取缓存的对象
* @param name 注册的回调函数name
* @param id id
* @param clz 返回的对象类型
* @param <T>
* @return
* @throws Exception
*/
public <T> T getObject(String name, int id, Class<T> clz) throws Exception {
return exec(name, id, clz);
}
}
测试类中,对上面的功能进行测试,代码如下:
@Test
public void testCacheClient() throws Exception {
int id = 1002;
String name = "cache_test";
// 注册回调函数
cacheClient.register(name, new CacheCallBackInterface(){
@Override
public String getKey(int id) {
return "test_" + id;
}
@Override
public int getExpire() {
return 30;
}
@Override
public Object getObject(String key) {
return key.length();
}
});
Integer count = cacheClient.getObject(name, id ,Integer.class);
logger.info("The count is : {}", count);
cacheService.setObject("test_" + id, 1231234, 30);
count = cacheClient.getObject(name, id, Integer.class);
logger.info("The count is : {}", count);
Thread.sleep(1000);
count = cacheClient.getObject(name, id, Integer.class);
logger.info("The count is : {}", count);
}
写代码的时候还是很清晰,知道应该怎么玩,而实际动手写这个流程的时候,还是有很多问题啊,有一些地方居然不清楚为什么要那么设计,为什么要那么玩,简直了,看来这一块了解的还是不够透彻,后面把这一块吃透后,得重写一遍
最后给出代码的git地址 : https://github.com/liuyueyi/java-web-archetype/tree/demo
(这个工程主要是一个简单的java web demo实例工程,会逐渐的向其中添加一些公用的组件(工作中get到什么,就往里面塞什么东西))