一、回调函数

为什么首先会讲回调函数呢?因为这个是理解监听器、观察者模式的关键。

什么是回调函数

所谓的回调,用于回调的函数。 回调函数只是一个功能片段,由用户按照回调函数调用约定来实现的一个函数。 有这么一句通俗的定义:就是程序员A写了一段程序(程序a),其中预留有回调函数接口,并封装好了该程序。程序员B要让a调用自己的程序b中的一个方法,于是,他通过a中的接口回调自己b中的方法。

举个例子:

这里有两个实体:回调抽象接口、回调者(即程序a)

回调接口(ICallBack )

public interface ICallBack {
public void callBack();
}

回调者(用于调用回调函数的类)

public class Caller {

    public void call(ICallBack callBack){
System.out.println("start...");
callBack.callBack();
System.out.println("end...");
} }

回调测试:

public static void main(String[] args) {
Caller call = new Caller();
call.call(new ICallBack(){ @Override
public void callBack() {
System.out.println("终于回调成功了!"); }
}); }

控制台输出:

start...

终于回调成功了!

end...

还有一种写法

ICallBack callBackB = new ICallBack(){
@Override
public void callBack() {
System.out.println("终于回调成功了!");
}
};
call.call(callBackB);

或实现这个ICallBack接口类

class CallBackC implements ICallBack{
@Override
public void callBack() {
System.out.println("终于回调成功了!");
}
}

有没有发现这个模型和执行一个线程,Thread很像。 没错,Thread就是回调者,Runnable就是一个回调接口。

new Thread(new Runnable(){
@Override
public void run() {
System.out.println("回调一个新线程!");
}}).start();

Callable也是一个回调接口,原来一直在用。 接下来我们开始讲事件监听器

二、事件监听模式

什么是事件监听器

监听器将监听自己感兴趣的事件一旦该事件被触发或改变,立即得到通知,做出响应。例如:Android程序中的Button事件。

Java的事件监听机制可概括为3点:

  1. Java的事件监听机制涉及到事件源,事件监听器,事件对象三个组件,监听器一般是接口,用来约定调用方式。
  2. 当事件源对象上发生操作时,它将会调用事件监听器的一个方法,并在调用该方法时传递事件对象过去。
  3. 事件监听器实现类,通常是由开发人员编写,开发人员通过事件对象拿到事件源,从而对事件源上的操作进行处理。

举个例子

这里我为了方便,直接使用JDK,EventListener 监听器,感兴趣的可以去研究下源码,非常简单。

监听器接口

public interface EventListener extends java.util.EventListener {
//事件处理
public void handleEvent(EventObject event);
}

事件对象

public class EventObject extends java.util.EventObject{
private static final long serialVersionUID = 1L;
public EventObject(Object source){
super(source);
}
public void doEvent(){
System.out.println("通知一个事件源 source :"+ this.getSource());
} }

事件源

事件源是事件对象的入口,包含监听器的注册、撤销、通知

public class EventSource {
//监听器列表,监听器的注册则加入此列表
private Vector<EventListener> ListenerList = new Vector<EventListener>();
//注册监听器
public void addListener(EventListener eventListener){
ListenerList.add(eventListener);
}
//撤销注册
public void removeListener(EventListener eventListener){
ListenerList.remove(eventListener);
}
//接受外部事件
public void notifyListenerEvents(EventObject event){
for(EventListener eventListener:ListenerList){
eventListener.handleEvent(event);
}
} }

测试执行

public static void main(String[] args) {
EventSource eventSource = new EventSource(); eventSource.addListener(new EventListener(){
@Override
public void handleEvent(EventObject event) {
event.doEvent();
if(event.getSource().equals("closeWindows")){
System.out.println("doClose");
}
} }); /*
* 传入openWindows事件,通知listener,事件监听器,
对open事件感兴趣的listener将会执行
**/
eventSource.notifyListenerEvents(new EventObject("openWindows")); }

控制台显示:

通知一个事件源 source :openWindows

通知一个事件源 source :openWindows

doOpen something...

到这里你应该非常清楚的了解,什么是事件监听器模式了吧。 那么哪里是回调接口,哪里是回调者,对!EventListener是一个回调接口类,handleEvent是一个回调函数接口,通过回调模型,EventSource 事件源便可回调具体监听器动作。

有了了解后,这里还可以做一些变动。 对特定的事件提供特定的关注方法和事件触发

public class EventSource {
...
  public void onCloseWindows(EventListener eventListener){
System.out.println("关注关闭窗口事件");
ListenerList.add(eventListener);
} public void doCloseWindows(){
this.notifyListenerEvents(new EventObject("closeWindows"));
}
...
}
public static void main(String[] args) {
    EventSource windows = new EventSource();
/**
* 另一种实现方式
*/
//关注关闭事件,实现回调接口
windows.onCloseWindows(new EventListener(){ @Override
public void handleEvent(EventObject event) {
event.doEvent();
if(event.getSource().equals("closeWindows")){
System.out.println("通过onCloseWindows来关注关闭窗口事件:并执行成功。 closeWindows");
} } }); //窗口关闭动作
windows.doCloseWindows(); }

这种就类似于,我们的窗口程序,Button监听器了。我们还可以为单击、双击事件定制监听器。

三、观察者模式

什么是观察者模式

观察者模式其实原理和监听器是一样的,使用的关键在搞清楚什么是观察者、什么是被观察者。

  • 观察者(Observer)相当于事件监器。有个微博模型比较好理解,A用户关注B用户,则A是B的观察者,B是一个被观察者,一旦B发表任何言论,A便可以获得。
  • 被观察者(Observable)相当于事件源和事件,执行事件源通知逻辑时,将会回调observer的回调方法update。

举个例子

为了方便,同样我直接使用JDK自带的Observer。

一个观察者

public class WatcherDemo implements Observer {
@Override
public void update(Observable o, Object arg) {
if(arg.toString().equals("openWindows")){
System.out.println("已经打开窗口");
}
}
}

被观察者

Observable是JDK自带的被观察者,具体可以自行看源码和之前的监听器事件源类似。

主要方法有:

  • addObserver()添加观察者,与监听器模式类似。
  • notifyObservers()通知所有观察者。

类Watched.java的实现描述:被观察者,相当于事件监听的事件源和事件对象。又理解为订阅的对象 主要职责:注册/撤销观察者(监听器),接收主题对象(事件对象)传递给观察者(监听器),具体由感兴趣的观察者(监听器)执行

/**
*
* 类Watched.java的实现描述:被观察者,相当于事件监听的事件源和事件对象。又理解为订阅的对象
* 主要职责:注册/撤销观察者(监听器),接收主题对象(事件对象)传递给观察者(监听器),具体由感兴趣的观察者(监听器)执行
* @author xuan.lx 2016年11月22日 下午3:52:11
*/
public class Watched extends Observable { public void notifyObservers(Object arg) { /**
* 为避免并发冲突,设置了changed标志位changed =true,则当前线程可以通知所有观察者,内部同步块会完了会设置为false;
通知过程中,正在新注册的和撤销的无法通知到。
*/
super.setChanged();
/**
* 事件触发,通知所有感兴趣的观察者
*/
super.notifyObservers(arg); } }

测试执行

public static void main(String[] args) {
Watched watched = new Watched();
WatcherDemo watcherDemo = new WatcherDemo();
watched.addObserver(watcherDemo);
watched.addObserver(new Observer(){
@Override
public void update(Observable o, Object arg) {
if(arg.toString().equals("closeWindows")){
System.out.println("已经关闭窗口");
}
}
});
//触发打开窗口事件,通知观察者
watched.notifyObservers("openWindows");
//触发关闭窗口事件,通知观察者
watched.notifyObservers("closeWindows"); }

控制台输出:

已经打开窗口

已经关闭窗口

总结

从整个实现和调用过程来看,观察者和监听器模式基本一样。

有兴趣的你可以基于这个模型,实现一个简单微博加关注和取消的功能。 说到底,就是事件驱动模型,将调用者和被调用者通过一个链表、回调函数来解耦掉,相互独立。

整个设计模式的初衷也就是要做到低耦合,低依赖。

再延伸下,消息中间件是什么一个模型?将生产者+服务中心(事件源)和消费者(监听器)通过消息队列解耦掉. 消息这相当于具体的事件对象,只是存储在一个队列里(有消峰填谷的作用),服务中心回调消费者接口通过拉或取的模型响应。 想必基于这个模型,实现一个简单的消息中间件也是可以的。

还比如Guava ListenableFuture,采用监听器模式就解决了future.get()一直阻塞等待返回结果的问题。

有兴趣的同学,可以再思考下观察者和责任链之间的关系, 我是这样看的。

同样会存在一个链表,被观察者会通知所有观察者,观察者自行处理,观察者之间互不影响。 而责任链,讲究的是击鼓传花,也就是每一个节点只需记录继任节点,由当前节点决定是否往下传。 常用于工作流,过滤器Web Filter。

转自:https://my.oschina.net/u/923324/blog/792857

Java设计模式补充:回调模式、事件监听器模式、观察者模式(转)的更多相关文章

  1. Java设计模式(三) 抽象工厂模式

    原创文章,同步发自作者个人博客,转载请注明出处 http://www.jasongj.com/design_pattern/abstract_factory/ 抽象工厂模式解决的问题 上文<工厂 ...

  2. Java设计模式(十二) 策略模式

    原创文章,同步发自作者个人博客,http://www.jasongj.com/design_pattern/strategy/ 策略模式介绍 策略模式定义 策略模式(Strategy Pattern) ...

  3. Java设计模式(二) 工厂方法模式

    本文介绍了工厂方法模式的概念,优缺点,实现方式,UML类图,并介绍了工厂方法(未)遵循的OOP原则 原创文章.同步自作者个人博客 http://www.jasongj.com/design_patte ...

  4. Java设计模式(一) 简单工厂模式不简单

    摘要:本文介绍了简单工厂模式的概念,优缺点,实现方式,以及结合Annotation和反射的改良方案(让简单工厂模式不简单).同时介绍了简单工厂模式(未)遵循的OOP原则.最后给出了简单工厂模式在JDB ...

  5. Java设计模式(十一) 享元模式

    原创文章,同步发自作者个人博客 http://www.jasongj.com/design_pattern/flyweight/.转载请注明出处 享元模式介绍 享元模式适用场景 面向对象技术可以很好的 ...

  6. Java设计模式(14)责任链模式(Chain of Responsibility模式)

    Chain of Responsibility定义:Chain of Responsibility(CoR) 是用一系列类(classes)试图处理一个请求request,这些类之间是一个松散的耦合, ...

  7. Java 设计模式系列(二三)访问者模式(Vistor)

    Java 设计模式系列(二三)访问者模式(Vistor) 访问者模式是对象的行为模式.访问者模式的目的是封装一些施加于某种数据结构元素之上的操作.一旦这些操作需要修改的话,接受这个操作的数据结构则可以 ...

  8. Java 设计模式系列(十)外观模式

    Java 设计模式系列(十)外观模式 门面模式(Facade):外部与一个子系统的通信必须通过一个统一的外观对象进行,为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这 ...

  9. Java 设计模式系列(九)组合模式

    Java 设计模式系列(九)组合模式 将对象组合成树形结构以表示"部分-整体"的层次结构.组合模式使得用户对单个对象的使用具有一致性. 一.组合模式结构 Component: 抽象 ...

随机推荐

  1. FFT学习及简单应用(一点点详细)

    什么是FFT 既然打开了这篇博客,大家肯定都已经对FFT(Fast Fourier Transformation)有一点点了解了吧 FFT即为快速傅里叶变换,可以快速求卷积(当然不止这一些应用,但是我 ...

  2. ACM_递推题目系列之二认错人(递推dp)

    递推题目系列之二认错人 Time Limit: 2000/1000ms (Java/Others) Problem Description: 国庆期间,省城HZ刚刚举行了一场盛大的集体婚礼,为了使婚礼 ...

  3. 解决:org.springframework.tuple.spel.TuplePropertyAccessor

    原来运行调试正常的项目,今天启动时报“java.lang.IllegalStateException: ApplicationEventMulticaster not initialized”错误.从 ...

  4. native2ascii命令详解

    1.native2ascii简介:    native2ascii是sun java sdk提供的一个工具.用来将别的文本类文件(比如*.txt,*.ini,*.properties,*.java等等 ...

  5. SQL server中的T-SQL语句

    首先点击新建查询 如下图所示 创建数据库:create database 数据库名称 使用数据库:use 数据库名称 创建表:create table 表名 ( 代码 ) 输入完成执行时需选中 如果需 ...

  6. redis之有序集合类型(Zset)——排行榜的实现

    当数据库对排序支持的不是很好,可以利用redis有序集合排序 原文链接:http://blog.csdn.net/loophome/article/details/50373202

  7. Appium Python API 汇总(中文版)

    网络搜集而来,留着备用,方便自己也方便他人.感谢总结的人! 1.contexts contexts(self): Returns the contexts within the current ses ...

  8. Python3.4的Pillow库实现验证码图片

    转自 http://blog.csdn.net/bin381/article/details/41969493 from PIL import Image,ImageDraw, ImageFont, ...

  9. Spring框架之控制反转和依赖注入

    学Spring框架必须理解控制反转和依赖注入.下面各自举一个例子,来说明控制反转和依赖注入. IOC(控制反转):应用本身创建和维护的依赖对象:现在交由外部容器(Spring)来创建和维护:这个控制权 ...

  10. 最优化方法系列:SGD、Adam

    整理一下资源,不过最好还是根据书上的理论好好推导一下..... 文章链接:Deep Learning 最优化方法之SGD 72615436 本文是Deep Learning 之 最优化方法系列文章 整 ...