觀察者模式的Java實現及應用

觀察者模式定義

码报开奖结果本期 www.iwqgw.icu 觀察者模式定義了對象之間的一對多依賴,這樣一來,當一個對象改變狀態時,它的所有依賴者都會收到通知并自動更新。

undefined

結構

關鍵字

  • Observable 
    即被觀察者,也可以被叫做主題(Subject)是被觀察的對象。通常有注冊方法(register),取消注冊方法(remove)和通知方法(notify)。

  • Observer 
    即觀察者,可以接收到主題的更新。當對某個主題感興趣的時候需要注冊自己,在不需要接收更新時進行注銷操作。

例子與應用

舉一個生活中的例子:比如用戶從報社訂閱報紙,報社和用戶之間是一對多依賴,用戶可以在報社訂閱(register)報紙,報社可以把最新的報紙發給用戶(notify),用戶自動收到更新。在用戶不需要的時候還可以取消注冊(remove)。

再比如Android中的EventBus,Rxjava的實現都是基于觀察者模式的思想。再比如回調函數:Android中對Button的點擊監聽等等。

觀察者模式可以用來解耦

自己用代碼實現一個觀察者模式

現在我們用代碼來實現上面訂閱報紙的例子:NewProvider作為對于報社的抽象,每隔兩秒鐘向用戶發送報紙;User作為用戶的抽象,可以收到報紙。NewsModel作為對報紙本身的抽象。

/**
 * 被觀察者接口定義
 */public interface MyObserverable {    void register(MyObserver myObserver);    void remove(MyObserver myObserver);    void send(NewsModel model);

}
/**
 * 觀察者接口定義
 */public interface MyObserver {    void receive(NewsModel model);

}
/**
 * 對于報社的抽象,實現了被觀察者接口,每隔2s發送一次報紙
 */public class NewsProvider implements MyObserverable {    private static final long DELAY = 2 * 1000;    private List<MyObserver> mObservers; //我們用一個List來維護所有的觀察者對象

    public NewsProvider() {
        mObservers = new ArrayList<>();
        generateNews();
    }    /**
     * 模擬產生新聞,每個2s發送一次
     */
    private void generateNews() {
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {            int titleCount = 1;            int contentCount = 1;            @Override
            public void run() {
                send(new NewsModel("title:" + titleCount++, "content:" + contentCount++));
            }
        }, DELAY, 1000);
    }    @Override
    public void register(MyObserver myObserver) {        if (myObserver == null)            return;        synchronized (this) {            if (!mObservers.contains(myObserver))
                mObservers.add(myObserver);
        }
    }    @Override
    public synchronized void remove(MyObserver myObserver) {
        mObservers.remove(myObserver);
    }    @Override
    public void send(NewsModel model) {        for (MyObserver observer : mObservers) {
            observer.receiveNews(model);
        }
    }
}
/**
 * 對于用戶的抽象
 */public class User implements MyObserver {    private String mName;    public User(String name) {
        mName = name;
    }    @Override
    public void receive(NewsModel model) {
        System.out.println(mName + " receive news:" + model.getTitle() + "  " + model.getContent());
    }
}
/**
 * 測試類
 */public class Test {    public static void main(String[] args) {

        NewsProvider provider = new NewsProvider();
        User user;        for (int i = 0; i < 10; i++) {
            user = new User("user:"+i);
            provider.register(user);
        }

    }
}

運行結果如下:

undefined

result

這樣我們就自己動手完成了一個簡單的觀察者模式。

其實在JDK的util包內Java為我們提供了一套觀察者模式的實現,在使用的時候我們只需要繼承Observable和Observer類即可,其實觀察者模式十分簡單,推薦閱讀JDK的實現代碼真心沒有幾行。此外在JDK的實現中還增加了一個布爾類型的changed域,通過設置這個變量來確定是否通知觀察者。

下面我們用JDK的類來實現一遍我們的觀察者模式:

public class NewsProvider extends Observable {    private static final long DELAY = 2 * 1000;    public NewsProvider() {
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {           private int titleCount = 1;           private int contentCount = 1;            @Override
            public void run() {
                setChanged(); //調用setChagned方法,將changed域設置為true,這樣才能通知到觀察者們
                notifyObservers(new NewsModel("title:" + titleCount++, "content:" + contentCount++));
            }
        }, DELAY, 1000);
    }
}
public class User implements Observer {    private String mName;    public User(String name) {
        mName = name;
    }    @Override
    public void update(Observable observable, Object data) {
        NewsModel model = (NewsModel) data;
        System.out.println(mName + " receive news:" + model.getTitle() + "  " + model.getContent());
    }
}

非常簡單有木有

回調函數與觀察者模式

關于回調函數的定義在知乎上看到過一個 很贊的解釋 :

你到一個商店買東西,剛好你要的東西沒有貨,于是你在店員那里留下了你的電話,過了幾天店里有貨了,店員就打了你的電話,然后你接到電話后就到店里去取了貨。在這個例子里,你的電話號碼就叫回調函數,你把電話留給店員就叫登記回調函數,店里后來有貨了叫做觸發了回調關聯的事件,店員給你打電話叫做調用回調函數,你到店里去取貨叫做響應回調事件?;卮鶩甌?。

在Android中我們有一個常用的回調:對與View點擊事件的監聽。現在我們就來分析一下對于View的監聽。通常在我們使用的時候是這樣的:

        xxxView.setOnClickListener(new View.OnClickListener() {            @Override
            public void onClick(View v) {                // do something
            }
        });

這樣我們就注冊好了一個回調函數。我們可以在View的源碼里發現這個接口:

    /**
     * Interface definition for a callback to be invoked when a view is clicked.
     */
    public interface OnClickListener {        /**
         * Called when a view has been clicked.
         *
         * @param v The view that was clicked.
         */
        void onClick(View v);
    }

當你setClickListener的時候在View的源碼中可以看到對本地OnClickListener的初始化

    /**
     * Register a callback to be invoked when this view is clicked. If this view is not
     * clickable, it becomes clickable.
     *
     * @param l The callback that will run
     *
     * @see #setClickable(boolean)
     */
    public void setOnClickListener(@Nullable OnClickListener l) {        if (!isClickable()) {
            setClickable(true);
        }
        getListenerInfo().mOnClickListener = l;
    }

當你的點擊到一個View后Android系統經過一系列的調用最后到了View的performClick方法中:

/**
     * Call this view's OnClickListener, if it is defined.  Performs all normal
     * actions associated with clicking: reporting accessibility event, playing
     * a sound, etc.
     *
     * @return True there was an assigned OnClickListener that was called, false
     *         otherwise is returned.
     */
    public boolean performClick() {        final boolean result;        final ListenerInfo li = mListenerInfo;        if (li != null && li.mOnClickListener != null) {
            playSoundEffect(SoundEffectConstants.CLICK);
            li.mOnClickListener.onClick(this);
            result = true;
        } else {
            result = false;
        }

        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);        return result;
    }

就在這里,觸發了你的onClick方法,然后執行方法體。

這里我們的被觀察者就是View,他的注冊方法(register)就是setOnClickListener(),通知方法就是performClick;而OnClickListener就是觀察者。只不過這里的只能注冊一個觀察對象而已。

來源:CSDN

上一篇: Java 異常處理的誤區和經驗總結

下一篇: 50個Java多線程面試題(上)

分享到: 更多
时时彩免费计划软件手机版 全天北京pk赛车两期计划 360足彩混合投注计算器 黑龙江时时中奖设 五分三大小单双稳赚买法 不思议棋牌 新疆时时玩法和技巧 吉子棋牌龙虎怎么赢 完整比分比分直播 500W双色球软件 重庆时时彩34567技巧 开一个体彩店要多少钱 麻将十赌九赢的小秘方 分分快3稳赚有谁用过 炸金花手机版下载安装 大公鸡七星彩奖表图