最近在學習netty的時候,發現裡面用到了監聽器模式,感覺非常實用,以前看設計模式的時候只是看,並沒有用上。其實這是一個非常重要並實用的設計模式,在很多框架裡面都用到了。 netty裡面的應用: 回調函數 為什麼先提到回調函數呢?因為回調函數是理解監聽器、觀察者模式的關鍵。剛畢業的時候老大也經常和我 ...
最近在學習netty的時候,發現裡面用到了監聽器模式,感覺非常實用,以前看設計模式的時候只是看,並沒有用上。其實這是一個非常重要並實用的設計模式,在很多框架裡面都用到了。
netty裡面的應用:
serverBootstrap.bind(8000).addListener(new GenericFutureListener<Future<? super Void>>() {
public void operationComplete(Future<? super Void> future) {
if (future.isSuccess()) {
System.out.println("埠綁定成功!");
} else {
System.err.println("埠綁定失敗!");
}
}
});
回調函數
為什麼先提到回調函數呢?因為回調函數是理解監聽器、觀察者模式的關鍵。剛畢業的時候老大也經常和我說Java中的回調。
回調函數,或簡稱回調(Callback 即call then back 被主函數調用運算後會返回主函數),是指通過函數參數傳遞到其它代碼的,某一塊可執行代碼的引用。這一設計允許了底層代碼調用在高層定義的子程式。
通俗的定義:就是程式員A寫了一段程式(程式a),其中預留有回調函數介面,並封裝好了該程式。程式員B要讓a調用自己的程式b中的一個方法,於是,他通過a中的介面回調自己b中的方法。
demo:這裡有兩個實體,回調抽象者介面、回調者(程式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 class CallDemo {
public static void main(String[] args) {
Caller caller = new Caller();
caller.call(() -> System.out.println("回調成功"));
}
}
- 控制台輸出
start...
回調成功
end...
這個模型和執行一個線程Thread很像。沒錯,Thread就是回調者,Runnable就是一個回調介面。
事件監聽器
事件監聽器就是自己監聽的事件一旦被觸發或改變,立即得到通知,做出響應。
Java的事件監聽機制可概括為3點:
- Java的事件監聽機制涉及到事件源,事件監聽器,事件對象三個組件,監聽器一般是介面,用來約定調用方式
- 當事件源對象上發生操作時,它將調用事件監聽器的一個方法,並將事件對象傳遞過去
- 事件監聽器實現類,通常是由開發人員編寫,開發人員通過事件對象拿到事件源,從而對事件源上的操作進行處理
這裡為了方便,直接用了 jdk,EventListener 監聽器
- 監聽器介面
public interface EventListener extends java.util.EventListener {
/**
* 事件處理
*/
void handleEvent(EventObject eventObject);
}
- 事件對象
public class EventObject extends java.util.EventObject {
public EventObject(Object source) {
super(source);
}
public void doEvent() {
System.out.println("通知一個事件源 source:" + this.getSource());
}
}
- 事件源
public class EventSource {
// 監聽器列表,監聽器的註冊 加入此列表
private List<EventListener> listeners = new ArrayList<>();
public void addListener(EventListener eventListener) {
listeners.add(eventListener);
}
public void removeListener(EventListener eventListener) {
listeners.remove(eventListener);
}
public void notifyListenerEvent(EventObject eventObject) {
for (EventListener eventListener : listeners) {
eventListener.handleEvent(eventObject);
}
}
}
- 測試執行
public class EventDemo {
public static void main(String[] args) {
EventSource eventSource = new EventSource(); // 事件源
eventSource.addListener(new EventListener() { // 事件源 調用監聽器的一個方法,並傳遞事件對象
@Override
public void handleEvent(EventObject eventObject) {
eventObject.doEvent();
if (eventObject.getSource().equals("closeWindow")) {
System.out.println("doClose"); // 回調
}
}
});
EventObject eventObject = new EventObject("closeWindow"); // 事件對象
eventSource.notifyListenerEvent(eventObject); // 觸發事件
}
}
- 控制台輸出
通知一個事件源 source:closeWindow
doClose
到這裡我們瞭解了什麼是監聽器模式了。EventListener是一個回調介面類,handleEvent是一個回調函數介面,通過回調模型,EventSource事件源便可回調具體監聽器動作。
觀察者模式
觀察者模式的原理其實和監聽器一樣,使用的關鍵在於搞清楚什麼是觀察者、什麼是被觀察者
- 觀察者相當於事件監聽
- 被觀察者相當於事件源和事件
為了方便,同樣直接使用jdk自帶的Observer
- 觀察者
public class Watcher implements Observer {
@Override
public void update(Observable o, Object arg) {
if (arg.toString().equals("openWindow")) {
System.out.println("打開視窗");
}
}
}
- 被觀察者
public class Watched extends Observable {
public void notifyObservers(Object arg) {
/**
* 為了避免併發衝突,設置了 changed 標誌位 changed=true,則當前線程可以通知所有觀察者,內部同步塊會設置為false;
* 通知過程中,正在新註冊的和撤銷的無法通知到
*/
super.setChanged();
/**
* 事件觸發,通知所有感興趣的觀察者
*/
super.notifyObservers(arg);
}
}
- 測試執行
public class WatcherDemo {
public static void main(String[] args) {
Watched watched = new Watched();
Watcher watcher = new Watcher();
watched.addObserver(watcher);
watched.addObserver(new Observer() {
@Override
public void update(Observable o, Object arg) {
if (arg.toString().equals("closeWindow")) {
System.out.println("關閉視窗");
}
}
});
watched.notifyObservers("openWindow");
watched.notifyObservers("closeWindow");
}
}
- 控制台輸出
打開視窗
關閉視窗
總結
從實現和調用過程來看,觀察者和監聽器模式基本一樣。基本上都是這個邏輯當事件源對象上發生操作時,它將調用事件監聽器的一個方法,並將事件對象傳遞過去,套用到觀察者模式上面就是,當被觀察者發生操作時,觀察者將根據被觀察者所做出的操作 進行對應的操作。
應用
- netty中監聽連接
- guava 中的 ListenableFuture,採用監聽器模型解決了原生JDK中 future.get() 一直阻塞結果的問題。
參考文章:https://my.oschina.net/u/923324/blog/792857