java設計模式--觀察者模式和事件監聽器模式
觀察者模式
觀察者模式又稱為訂閱—釋出模式,在此模式中,一個目標物件管理所有相依於它的觀察者物件,並且在它本身的狀態改變時主動發出通知。這通常透過呼叫各觀察者所提供的方法來實現。此種模式通常被用來事件處理系統。。
基於事件驅動機制的系統或語言,比如node.js、nio等,不難發現其最終的基礎模式就是觀察者模式,只是不同的應用場景,也會有各自不同的側重。
觀察者
class Watcher implements java.util.Observer { public void update(java.util.Observable obj, Object arg) { System.out.println("Update() called, count is " + ((Integer) arg).intValue()); } }
被觀察者
class BeingWatched extends java.util.Observable { void counter(int period) { for(; period>=0; period-- ) { setChanged(); notifyObservers(new Integer(period)); try { Thread.sleep(100); } catch( InterruptedException e) { System.out.println("Sleep interrupeted" ); } } } };
測試
public class ObserverDemo {
public static void main(String[] args) {
BeingWatched beingWatched = new BeingWatched();//受查者
Watcher watcher = new Watcher();//觀察者
beingWatched.addObserver(watcher);
beingWatched.counter(10);
}
}
監聽器模式
事件源經過事件的封裝傳給監聽器,當事件源觸發事件後,監聽器接收到事件物件可以回撥事件的方法
1、首要定義事件源物件(事件源相當於單擊按鈕事件當中的按鈕物件、屬於被監聽者):
public class DemoSource {
private Vector repository = new Vector();//監聽自己的監聽器佇列
public DemoSource(){}
public void addDemoListener(DemoListener dl) {
repository.addElement(dl);
}
public void notifyDemoEvent() {//通知所有的監聽器
Enumeration enum = repository.elements();
while(enum.hasMoreElements()) {
DemoListener dl = (DemoListener)enum.nextElement();
dl.handleEvent(new DemoEvent(this));
}
}
}
2、其次定義事件(狀態)物件(該事件物件包裝了事件源物件、作為引數傳遞給監聽器、很薄的一層包裝類):
public class DemoEvent extends java.util.EventObject {
public DemoEvent(Object source) {
super(source);//source—事件源物件—如在介面上發生的點選按鈕事件中的按鈕
//所有 Event 在構造時都引用了物件 "source",在邏輯上認為該物件是最初發生有關 Event 的物件
}
public void say() {
System.out.println("This is say method...");
}
}
3、最後定義我們的事件偵聽器介面如下
public interface DemoListener extends java.util.EventListener {
//EventListener是所有事件偵聽器介面必須擴充套件的標記介面、因為它是無內容的標記介面、
//所以事件處理方法由我們自己宣告如下:
public void handleEvent(DemoEvent dm);
}
監聽器實現類
public class DemoListener1 implements DemoListener {
public void handleEvent(DemoEvent de) {
System.out.println("Inside listener1...");
de.say();//回撥
}
}
4、測試程式碼public class TestDemo {
DemoSource ds;
public TestDemo(){
try{
ds = new DemoSource();
//將監聽器在事件源物件中登記:
DemoListener1 listener1 = new DemoListener1();
ds.addDemoListener(listener1);
ds.addDemoListener(new DemoListener() {
public void handleEvent(DemoEvent event) {
System.out.println("Method come from 匿名類...");
}
});
ds.notifyDemoEvent();//觸發事件、通知監聽器
}catch(Exception ex){
ex.printStackTrace();
}
}
public static void main(String args[]) {
new TestDemo();
}
}
總結
監聽器模式是觀察者模式的另一種形態,同樣基於事件驅動模型。監聽器模式更加靈活,可以對不同事件作出相應。但也是付出了系統的複雜性作為代價的,因為我們要為每一個事件源定製一個監聽器以及事件,這會增加系統的負擔。
釋出訂閱
觀察者模式實現釋出訂閱,可以新增一個排程中心,降低publisher和subscriber的耦合,具體實現中,有兩個版本。
1) 拉模式:目標角色在發生變化後,僅僅告訴觀察者角色“我變化了”;觀察者角色如果想要知道具體的變化細節,則就要自己從目標角色的介面中得到。拉模式是想要就主動表白獲取。
2) 推模式:通知你發生變化的同時,通過一個引數將變化的細節傳遞到觀察者角色中去。推模式是管你要不要,先給你啦。
這兩種模式的使用,取決於系統設計時的需要。如果目標角色比較複雜,並且觀察者角色進行更新時必須得到一些具體變化的資訊,則“推模式”比較合適。如果目標角色比較簡單,則“拉模式”就很合適啦。
事件和訊息的區別
事件本身即是具有特定業務含義的一種固定結構物件,而訊息是資料傳輸過程中的載體。概念上寬泛來講,事件可以稱作是一種訊息,而訊息不能代替事件。事件反映的是特定的業務狀態,比如訂單建立、服務呼叫失敗、應用宕機等。
一個事件物件描述的是誰在什麼時間做了什麼事情,看到這個物件,我們就能知道是發生了什麼特定的事情。但是事件物件本身不承載資料傳遞的職能。
訊息中介軟體實現的是訊息的儲存,解決的是解耦上下游業務系統。事件處理系統是更多的側重對事件的分析處理,並驅動業務的進一步扭轉。