關於設計模式,以前筆者寫過工廠模式,最近在使用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。