关于设计模式,以前笔者写过工厂模式,最近在使用gava ListenableFuture时发现事件监听模型特别有意思,因而就把事件监听、观察者之间比较了一番,发现这是一个很是重要的设计模式,在不少框架里扮演关键的做用。java
为何首先会讲回调函数呢?由于这个是理解监听器、观察者模式的关键。android
所谓的回调,用于回调的函数。 回调函数只是一个功能片断,由用户按照回调函数调用约定来实现的一个函数。 有这么一句通俗的定义:就是程序员A写了一段程序(程序a),其中预留有回调函数接口,并封装好了该程序。程序员B要让a调用本身的程序b中的一个方法,因而,他经过a中的接口回调本身b中的方法。程序员
这里有两个实体:回调抽象接口、回调者(即程序a)web
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("终于回调成功了!"); } }); }
控制台输出:windows
start...设计模式
终于回调成功了!并发
end...框架
还有一种写法ide
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点:
这里我为了方便,直接使用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监听器了。咱们还能够为单击、双击事件定制监听器。
观察者模式其实原理和监听器是同样的,使用的关键在搞清楚什么是观察者、什么是被观察者。
为了方便,一样我直接使用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自带的被观察者,具体能够自行看源码和以前的监听器事件源相似。
主要方法有
类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"); }
控制台输出:
已经打开窗口
已经关闭窗口
从整个实现和调用过程来看,观察者和监听器模式基本同样。
有兴趣的你能够基于这个模型,实现一个简单微博加关注和取消的功能。 说到底,就是事件驱动模型,将调用者和被调用者经过一个链表、回调函数来解耦掉,相互独立。
“你别来找我,有了我会找你”。
整个设计模式的初衷也就是要作到低耦合,低依赖。
再延伸下,消息中间件是什么一个模型? 将生产者+服务中心(事件源)和消费者(监听器)经过消息队列解耦掉. 消息这至关于具体的事件对象,只是存储在一个队列里(有消峰填谷的做用),服务中心回调消费者接口经过拉或取的模型响应。 想必基于这个模型,实现一个简单的消息中间件也是能够的。
还好比gava ListenableFuture,采用监听器模式就解决了future.get()一直阻塞等待返回结果的问题。
有兴趣的同窗,能够再思考下观察者和责任链之间的关系, 我是这样看的。
一样会存在一个链表,被观察者会通知全部观察者,观察者自行处理,观察者之间互不影响。 而责任链,讲究的是击鼓传花,也就是每个节点只需记录继任节点,由当前节点决定是否往下传。 经常使用于工做流,过滤器web filter。