基于回调的观察者模式
文章目录
- 回调机制的简单的理解:
- 比较经典的回调方式:
- 示例
- 基于监听的事件处理
- 回调的简单理解
- 同步回调
- 异步回调函数
- Java回调机制进阶
- 回调进阶(基于回调的"观察者"模式的实现)
- 回调机制和监听机制实现事件触发功能的区别
- 事件机制的简单的理解
- 事件处理模型中,主要涉及三类对象:
- 事件驱动编程
- epoll原理
- 基于监听的事件处理&基于回调的事件处理
回调机制的简单的理解:
A类通过某函数实现功能,但A的执行却并不取决于A,而是由B来决定。这时通常将函数的入口地址当作参数传递到B类
比较经典的回调方式:
A的回调方法是由B中的方法调用的,使用时传入回调方法即可。
————————————————————————————————————————
class A实现接口CallBack callback
class A中关联一个class B的引用b //此步很关键,相当于classA注册classB
class B有一个参数为callback的方法f(CallBack callback)
A调用B的方法 f(CallBack callback) ——A类调用B类的某个方法 C
然后B就可以在f(CallBack callback)方法中调用A的方法 ——B类调用A类的某个方法D
————————————————————————————————————————
示例
现有一农场需要向气象局订阅天气预报信息。农场向气象局发出订阅请求,气象局接受农场的订阅请求后,
每天都会向农场推送后一天的天气信息。农场每天接受到天气预报信息,将做对应的生产安排,具体安排
如下:如果气温在010℃,播种小麦,如果气温在1115℃播种大豆,如果气温在16~20℃播种棉花,否则
维护农场设备。
我们从“案例1”中可以提取回调的概念。
农场接收到天气预报信息后,才会进行“工作安排”,由于农场不知道天气预报信息返回的精确时间,因此进行
“工作安排”的时机实际是由气象局决定的。自然地,我们想到将“工作安排”用一个函数(func())来实现,并且该函数的
具体实现由农场(Farm类)来实施,而函数的调用位置及调用时机由气象局(MeteorologicalBureau类)来决定。这就是一个典型的回调场景,
监听模型分析:气象局发送天气信息为事件源
天气信息为事件
农场接收消息并做相应处理为监听器
回调模型分析:农场做工作安排,但工作安排是依赖于气象局发送的天气好坏的决定的,所以工作安排就是回调函数.
结论:农场实现了回调函数,通过传递回调接口到气象局后,回调函数调用时机是气象局来决定的
也就是说,通过农场将回调函数传递给气象局,实现了气象局预测天气信息——触发执行——>农场的回调函数
基于监听的事件处理
JAVA事件:根据不同的事件类型调用不同的处理函数
监听机制是a发生事件触发多个监听器bcd来执行事件处理函数
## 气象局发布气象信息:事件源
public class 气象局{农场 nc;气象局(农场 nc){ //注册观察者this.nc=nc;}void predirect(){new Thread(new Runnable(){public void run(){try{Thread.sleep(10000); //模拟耗时操作}catch(Exception e){e.printStackTrace();}maxTemperature=100;minTemperature=1;syso("气象局发布天气信息:最高气温:"+maxTemperature+"最低气温"+minTemperature);nc.call(maxTemperature,minTemperature); //工作安排}}).start();}
}
## 农场为事件监听器:监听天气信息,进行工作安排
public class 农场{//定义回调函数void call(int maxTemperature,int minTemperature){//根据不同的温度做不同的工作安排}}
public static void main(){//1. 监听器,监听气象局的气象信息农场 nc=new 农场();//2. 事件源注册监听器气象局 qxj=new 气象局(nc);//3. 气象局进行预测qxj.predirect();
}
回调的简单理解
假设有两个类,类A有一个方法a(),类B有一个方法b(),类A调用类B的方法b(),而方法b()回调了方法a()
回调就想一个约定,如果我们调用了b,那么就必须要回调,而不需要显示调用
我们用例子来解释:小明和小李相约一起去吃早饭,但是小李起的有点晚要先洗漱,等小李洗漱完成后,通知小明再一起去吃饭。小明就是类A,小李就是类B。一起去吃饭这个事件就是方法a(),小李去洗漱就是方法b()。
同步回调
public interface Callable{public void togetherEat(String food); //定义回调接口
}
小明
public class XiaoMing implements EatRice{//小明//小李先洗脸,然后小明和小李一起吃public void eatFood() {XiaoLi xl = new XiaoLi();//A调用B的方法,则自动回调A的方法xl.washFace("大龙虾", this);//this指的是小明这个类实现的EatRice接口}//重写回调函数@Overridepublic void eat(String food) {// TODO Auto-generated method stubSystem.out.println("小明和小李一起去吃" + food);}
}
小李
public class XiaoLi{public void washFace(String food,EatRice er){System.out.println("小李要洗漱");//B回调A的方法er.eat(food);}
}
测试Demo
public class demo {public static void main(String args[]) {XiaoMing xm = new XiaoMing();xm.eatFood();}
}
异步回调函数
小李洗脸的时候,小明玩手机
public interface Callable{public void togetherEat(String food); //定义回调接口
}
小明
public class XiaoMing implements EatRice{//小明//小李先洗脸,然后小明和小李一起吃public void eatFood() {XiaoLi xl = new XiaoLi();//A调用B的方法,则自动回调A的方法xl.setEatRiceListener(this, "大龙虾"); //this指的是小明这个类实现的EatRice接口}//重写回调函数@Overridepublic void togetherEat(String food) {// TODO Auto-generated method stubSystem.out.println("小明和小李一起去吃" + food);}
}
小李
public class XiaoLi{private EarRice er;public void setEatRiceListener(EatRice er, String food) {this.er = er;washFace(food);}public void washFace(String food,EatRice er){System.out.println("小李要洗漱");//B异步回调A的方法new Thread(new Runnable(){// TODO Auto-generated method stubtry {//小李洗漱的同时小明玩手机,开启线程实现异步play();Thread.sleep(10000);System.out.print("10秒后 ");//B调用A的方法er.eat(food);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}).start();er.togetherEat(food);}//小明玩手机public void play() {System.out.println(" 小明要玩手机");}}
测试Demo
public class demo {public static void main(String args[]) {XiaoMing xm = new XiaoMing();xm.eatFood(); //类A调用类B的方法,自动会调用回调函数}
}
参考优秀博客
Java回调机制进阶
假设有两个类,类A有一个方法a(),类B有一个方法b(),类A调用类B的方法b(),而方法b()回调了方法a()。
深入理解:
类A实现回调方法a(),但a的调用却依赖于类B方法的调用类A调用类B的方法,自动会调用回调函数
结构
class A实现接口CallBack callback
class A中关联一个class B的引用b
class B有一个带callback的参数,一般会传入this
操作
A调用B带有callback参数的方法(CallBack callback) //A把回调函数注册/传递给B,让B进行调用
然后B就可以在f(CallBack callback) //调用回调函数
## 1. 声明天气预报发布接口,通过内部实现类订阅和发布
## 2. 创建气象局,该类接受天气预告信息的订阅和发布天气预报信息
## 3. 创建农场类,该类订阅天气预报信息,并在接受到天气预报信息后做工作安排将内部类对象传递给气象局对象==将farm类的onRelease()传递给气象局对象
## 4. 编写测试类,首先new一个农场对象并订阅天气预报信息,然后气象局调用predict()函数预测天气并发布预报
## 5. 运行,结果如下:
2019-02-06 21-54-59,农场订阅天气预报信息
2019-02-06 21-55-08,农场接收到天气信息:明日渝北区气温8~10℃,风力5~8级
2019-02-06 21-55-08,农场明日工作安排:播种小麦
分析:工作安排依赖于天气预报信息,所以定义它为回调函数
代码实现
## 总目标:实现天气预告信息的订阅和发布,天气预告信息发布后做工作安排
## 订阅:农场的发布监听器对象传入气象局,实例化用于预测天气的类。但是预测数据任然是气象局提供的
## 定义回调函数
public interface Callable{void doAfterReceiveWheatherInfo(int minTemperature, int maxTemperature);
}
public class Farm implements Callable{private MeteorologicalBureau mBureau; //气象局private MeteorologicalBureauListener mBureauListener; //这个农场注册用的,将其注册到气象局==气象局有了发布方法 public Farm(MeteorologicalBureau bureau){mBureau = bureau;}/*** 订阅天气信息*/public void subscribe(){System.out.println(TestUtils.getTimeStamp() + "," + "农场订阅天气预报信息");mBureauListener = new MeteorologicalBureauListener() {public void onRelease(String description, int minTemperature,int maxTemperature, int minWindscale, int maxWindscale) {System.out.println(TestUtils.getTimeStamp() + "," + "农场接收到天气信息:" + description);}};mBureau.register(mBureauListener); //通过一个发布对象,注册农场到气象局mBureau.setProperty(Callable call); //调用类B的b方法,将回调函数传入}/*** 接收到气象局发布的天气信息后,农场做对应的工作安排* @param minTemperature 明日最低气温* @param maxTemperature 明日最高气温* 调用时机取决于气象局,农场类只负责*/private void doAfterReceiveWheatherInfo(int minTemperature, int maxTemperature){String timeStamp = TestUtils.getTimeStamp();if(minTemperature >= 0 && minTemperature <= 10){System.out.println(timeStamp + "," + "农场明日工作安排:播种小麦");}else if(minTemperature >= 11 && minTemperature <= 15){System.out.println(timeStamp + "," + "农场明日工作安排:播种大豆");}else if(minTemperature >= 16 && minTemperature <= 20){System.out.println(timeStamp + "," + "农场明日工作安排:播种棉花");}else{System.out.println(timeStamp + "," + "农场明日工作安排:维护设备");}}
}## 声明天气预报发布接口,通过内部实现类订阅和发布
public interface MeteorologicalBureauListener {void onRelease(String description, //天气预报信息描述int minTemperature, int maxTemperature,int minWindscale, //最低风力int maxWindscale); //最高风力
}
## 定义气象局-类B
/*** 气象局(天气预报信息发布者)* @author WenYong*/
public class MeteorologicalBureau {private MeteorologicalBureauListener mListener; //提供订阅发布功能private Callable call;void setProperty(Callable call){this.call=call;}/*** 注册对"气象局"类监听* @param listener*/public void register(MeteorologicalBureauListener listener){if(null == listener){return;}mListener = listener;}/*** 取消订阅天气信息*/public void unsubscribe(){mBureau.unregister(mBureauListener);}/*** 取消注册对"气象局"类监听* @param listener*/public void unregister(MeteorologicalBureauListener listener){if(null == listener){return;}if(mListener.equals(listener)){mListener = null;}}/*** 天气信息预测*/public void predict(){new Thread(new Runnable() {public void run() {try {// 模拟耗时操作Thread.sleep(9000);} catch (InterruptedException e) {e.printStackTrace();}mListener.onRelease("明日渝北区气温8~10℃,风力5~8级", 8, 10, 5, 8);}}).start();call.doAfterReceiveWheatherInfo(); //天气预告发布后,调用回调函数}
}
## Demo测试
public class Test {public static void main(String[] args) {// 创建气象局MeteorologicalBureau bureau = new MeteorologicalBureau();// 农场订阅天气信息,注入listenernew Farm(bureau).subscribe();// 气象局进行天气信息预测,自动调用回调函数bureau.predict();}
}
回调进阶(基于回调的"观察者"模式的实现)
在学会了回调的基本使用方法后,我们将案例1稍加修改,增加一个天气预报订阅者
案例2
现有一农场和一机场需要向气象局订阅天气预报信息。农场和机场向气象局发出订阅请求,气象局接受订阅请求后,
每天都会向农场和机场推送后一天的天气信息。农场每天接受到天气预报信息,将做对应的生产安排,具体安排
如下:如果气温在010℃,播种小麦,如果气温在1115℃播种大豆,如果气温在16~20℃播种棉花,否则
维护农场设备;机场接收到天气预报信息,将采取对应的运营管理措施,具体如下:如果风力小于5级,不做预警正常起飞,
如果风力5~8级,预警起飞,如果风力大于8级,暂停起飞。
观察者模式:农场和机场作为“观察者”,向气象局订阅天气预报信息
气象局作为“目标着”提供天气预报信息
创建气象局类MeteorologicalBureau,该类负责接受天气预报信息的订阅和发布天气预报信息
并发队列来存储机场和农场传递过来的MeteorologicalBureauListener实现对象的引用
## 创建机场类
/*** 机场(天气预报信息订阅者)* @author WenYong**/
public class Airport extends Callable{private MeteorologicalBureau mBureau; //订阅天气的气象局private MeteorologicalBureauListener mBureauListener; //订阅天气的public Airport(MeteorologicalBureau bureau){mBureau = bureau;}/*** 订阅天气信息*/public void subscribe(){System.out.println(TestUtils.getTimeStamp() + "," + "机场订阅天气预报信息");mBureauListener = new MeteorologicalBureauListener() {public void onRelease(String description, int minTemperature,int maxTemperature, int minWindscale, int maxWindscale) {System.out.println(TestUtils.getTimeStamp() + "," + "机场接收到天气信息:" + description);//doAfterReceiveWheatherInfo(minWindscale, maxWindscale);}};mBureau.register(mBureauListener);mBureau.setProperty(Callable call);}/*** 取消订阅天气信息*/public void unsubscribe(){mBureau.unregister(mBureauListener);}/*** 接收到气象局发布的天气信息后,机场做对应的运营管理措施* @param minWindscale* @param maxWindscale*/private void doAfterReceiveWheatherInfo(int minWindscale, int maxWindscale){String timeStamp = TestUtils.getTimeStamp();if(maxWindscale < 5){System.out.println(timeStamp + "," + "机场明日运营管理措施:不做预警正常起飞");}else if(minWindscale >= 5 && maxWindscale <= 8){System.out.println(timeStamp + "," + "机场明日运营管理措施:预警起飞");}else{System.out.println(timeStamp + "," + "机场明日运营管理措施:暂停起飞");}}}
/*** 气象局(天气预报信息发布者)* @author WenYong**/
public class MeteorologicalBureau {private ConcurrentLinkedQueue<MeteorologicalBureauListener> mListenerQueue;private Callable call;public MeteorologicalBureau(){mListenerQueue = new ConcurrentLinkedQueue<>();}void setProperty(Callable call){this.call=call;}/*** 注册对"气象局"类监听* @param listener*/public void register(MeteorologicalBureauListener listener){if(null == listener){return;}if(mListenerQueue.contains(listener)){return;}mListenerQueue.add(listener);}/*** 取消注册对"气象局"类监听* @param listener*/public void unregister(MeteorologicalBureauListener listener){if(null == listener){return;}if(!mListenerQueue.contains(listener)){return;}mListenerQueue.remove(listener);}/*** 天气信息预测*/public void predict(){new Thread(new Runnable() {public void run() {try {// 模拟耗时操作Thread.sleep(9000);} catch (InterruptedException e) {e.printStackTrace();}release(mListenerQueue, "明日渝北区气温8~10℃,风力5~8级", 8, 10, 5, 8);}}).start();this.call.doAfterReceiveWheatherInfo();}/*** 天气预报信息发布* @param queue 监听队列* @param description 天气预报描述* @param minTemperature 最低气温* @param maxTemperature 最高气温* @param minWindscale 最低风力* @param maxWindscale 最高风力*/private void release(ConcurrentLinkedQueue<MeteorologicalBureauListener> queue,String description, int minTemperature,int maxTemperature,int minWindscale,int maxWindscale){if(null == queue || queue.isEmpty()){return;}Iterator<MeteorologicalBureauListener> it = queue.iterator();while(it.hasNext()){it.next().onRelease(description, minTemperature, maxTemperature, minWindscale, maxWindscale);}}}
## Demo测试类
public class Test {public static void main(String[] args) {MeteorologicalBureau bureau = new MeteorologicalBureau();// 农场订阅天气信息new Farm(bureau).subscribe();// 机场订阅天气信息new Airport(bureau).subscribe();// 气象局进行天气信息预测bureau.predict();}
}
总结:农场订阅天气信息时,可以将回调函数写到发布函数中,直到predict才会真正调用到发布函数。
回调机制和监听机制实现事件触发功能的区别
共同点都实现事件触发的功能
监听机制是气象局关联多个农场,当预测天气时调用工作函数 气象局关联多个农场,当发生事件循环通知多个农场
事件机制的简单的理解
引发的事件不由引发事件的对象自己处理,而是委派给独立事件处理对象负责。观察者模式的委派事件模型
将事件监听器注册到按钮上——>我单机按钮触发ActionEvent事件——>将事件传递给事件监听器并进行相应处理
事件处理模型中,主要涉及三类对象:
1.EventSource(事件源)。事件发生的场所,通常就是各个组件,比如按钮、窗口、菜单。
2.Event(事件)。事件封装了事件发生的相关信息。
3.EventListener(事件监听器)。监听事件源发生的事件,并对事件作出相应的响应。
事件驱动编程
事件驱动编程是一种编程范式,这里程序的执行流由外部事件来决定。它的特点是包含一个事件循环,当外部事件发生时使用回调机制来触发相应的处理。另外两种常见的编程范式是(单线程)同步以及多线程编程。
epoll原理
epoll事先通过epoll_ctl()来注册一个文件描述符,一旦基于某个文件描述符就绪时,内核会采用类似callback的回调机制,迅速激活这个文件描述符,当进程调用epoll_wait()时便得到通知。
基于监听的事件处理&基于回调的事件处理
Android提供了两套事件处理机制:
1.基于监听的事件处理。
2.基于回调的事件处理。
基于监听的事件处理就是在android的组件上绑定特定的监听器
基于回调的事件处理就是重写UI组件或者Activity的回调方法。
基于回调的事件处理用于处理一些具有通用性的事件,基于监听的事件处理用于处理与具体业务相关的事件。
总结:回调机制和监听机制的实质都是触发的一种实现,回调机制通过b给a传递回调接口,实现了a发生事件触发b的回调接口执行。而监听机制是a发生事件触发多个监听器bcd来执行事件处理函数。也就是说,回调机制本质上是一种特殊的监听机制
基于监听的事件处理是一种委派式(Delegation)的事件处理方式。UI组件(事件源)将发生的事件委派给特定的对象(监听器)处理,这很类似于人类社会的分工协作。例如某商场发生火灾时,商场并不会处理该事件,而是交给消防局处理;商场发生斗殴事件时,就交给公安局处理。商场可以将不同的事件委派给不同的机构处理,同时消防局和公安局也可以对各种场所进行监听并且处理发生的事件。这种委派式的事件处理方式将事件源和监听器分离,能提高程序的可维护性。
基于回调的观察者模式相关推荐
- Android移动开发之【通往安卓的神奇之旅】基于回调的事件处理
文章目录 1 Android中的事件处理方法 2 基于回调的事件处理机制详解 1 Android中的事件处理方法 事件处理:响应用户UI动作,提高应用程序交互性 1.基于监听的事件处理机制 2.基于回 ...
- Android基于回调的事件处理
基于回调的事件处理模型更加简单: 如果说事件监听机制是一种委托式的事件处理,那么回调机制则恰好与之相反:对于基于回调的时间处理模型来说,事件源和事件监听器是统一的,或者说事件监听器完全消失了.当用户在 ...
- 基于spring的观察者模式
简单的说,观察者模式,就类似于 广播站发送广播,和收音机的关系.多个收音机去收听同一个广播频道. 在实际的业务场景中,可以是这样的.创建订单成功后,发布事件.然后减库存.发送短信.调用微信.调用物流服 ...
- 关于Java“回调”的详细理解及使用
本文由"言念小文"原创,转载请说明文章出处 一.前言 接触java快六年了,从事软件开发工作也三年多了.感谢那些知名或不知名的大神一篇篇精彩博文,帮助我 从一个开发小白一步步成长为 ...
- Java设计模式-回调函数和观察者模式
Android的框架中有非常多的地方使用了回调的模式,例如Activity的生命周期,按钮的点击事件等. 下面是回调的基本模型: public class A {private CallBack ca ...
- 深入浅出: Java回调机制(异步)
什么是回调?今天傻傻地截了张图问了下,然后被陈大牛回答道"就一个回调-".此时千万个草泥马飞奔而过(逃 哈哈,看着源码,享受着这种回调在代码上的作用,真是美哉.不妨总结总结. 一. ...
- javascript的回调函数 同步 异步
后一个任务等待前一个任务结束再执行.程序执行顺序与任务排列顺序一致的,同步的. 参考: http://www.ruanyifeng.com/blog/2012/12/asynchronous%EF%B ...
- java filter 回调_Java 异步回调机制实例分析
Java 异步回调机制 一.什么是回调 回调,回调.要先有调用,才有调用者和被调用者之间的回调.所以在百度百科中是这样的: 软件模块之间总是存在着一定的接口,从调用方式上,可以把他们分为三类:同步调用 ...
- java 异步通知_Java 异步回调机制实例解析
什么是回调?今天傻傻地截了张图问了下,然后被陈大牛回答道"就一个回调-".此时千万个草泥马飞奔而过 哈哈,看着源码,享受着这种回调在代码上的作用,真是美哉.不妨总结总结. 一.什么 ...
最新文章
- Android OpenGL ES(十一)绘制一个20面体 .
- SHELL简单脚本编写
- stm32-USART1重映射
- bat批处理命令大全_DOS使用环境变量图文教程,bat批处理脚本查看调用环境变量命令...
- python小结_python简单小结
- Servlet与缓存
- C语言程序判断计算机的CPU大小端
- C#多线程学习(三) 生产者和消费者 (转载系列)——继续搜索引擎研究
- 程序员操作系统推荐_程序员的这些问题,竟然在工作后才发现!
- Openresty 安装、源码编译,增加WAF模块naxsi
- linux文件名变量,文件名通配符、变量以及管道知识点的总结
- MySQL数据库中数据完整性_MySQL数据完整性详细讲解及实现方式
- java编写数独计算器
- specular BRDF
- linux无法识别raid,linux – 无法从失败的RAID中恢复
- 发一个自己常用的通信词汇缩写表(实时更新)
- PIX for Windows使用
- 计算机专业必须知道的东西——C语言的发展
- python pip 安装使用国内镜像源
- 微信小程序之蓝牙通信模块
热门文章
- 群体智能——激发更多潜能
- 百度2015校园招聘面试题(成功拿到offer)
- 想知道阿猫阿狗的日常
- html 设置整体字体,HTML字体的设置
- java 抽象类 模板_java抽象类的体现-模板模式
- 有三宠选择的java游戏_口袋妖怪GO御用三宠哪家强 初始宠物选择推荐
- 开源?结缘!Towhee 开源社区与上海人工智能实验室 OpenDataLab 成为开源生态合作伙伴
- SVN搭建文件服务器
- Android 监听系统来电获取来电信息
- ios wkweb设置图片_在iOS中使用WKWebView如何支持展示webp格式图片(包括本地html)?...