Java設計模式補充:回調模式、事件監聽器模式、觀察者模式(轉)


一、回調函數

為什么首先會講回調函數呢?因為這個是理解監聽器、觀察者模式的關鍵。

什么是回調函數

所謂的回調,用於回調的函數。 回調函數只是一個功能片段,由用戶按照回調函數調用約定來實現的一個函數。 有這么一句通俗的定義:就是程序員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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM