1. 程式人生 > >java 設計模式 觀察者模式 新聞訊息推送

java 設計模式 觀察者模式 新聞訊息推送

觀察者模式,字面意思有個觀察者,那麼就應該有一個被觀察者。兩個定義:
觀察者:Observer (比如新聞客戶端,你自己的微訊號)
被觀察者:Observable(新聞推送端,你關注的微信公眾號)
1.觀察者可以同時訂閱多個被觀察者。
2.被觀察者可以同時被多個觀察者訂閱。
3.被觀察者發生改變時會影響到所有的觀察者。
這裡寫圖片描述
對於每個使用者都存在這三條線路。
Java中已經幫我們實現了觀察者模式,藉助於java.util.Observable和java.util.Observer。
接下來程式碼時間。
需求:新聞推送端News,需要向某個客戶ClientX端推送訊息,客戶端可以選擇去開啟或者關閉推送功能。
新聞推送端News程式碼

package test;

import java.util.Observable;

public class News extends Observable{

    private String news;

    public String getNews() {
        return news;
    }

    public void setNews(String news) {
        this.news = news;
        //發生改變
        setChanged();
        //推送訊息到客戶端
        notifyObservers();
    }
}

新聞客戶端程式碼

package test;

import java.util.Observable;
import java.util.Observer;

public class ClientX implements Observer {

    /**
     * 開啟推送功能
     * @param observable  被觀察者物件(這裡是新聞推送端)
     */
    public void openPushMsg(Observable observable)  
    {  
        //新增到觀察者列表
        observable.addObserver(this
); } /** * 實現介面重新的方法 * 有新訊息推送來時,會被呼叫 */ public void update(Observable observable, Object arg) { if(observable instanceof News){//判斷是否來自新聞推送端 News news=(News) observable; System.out.println("現在最新訊息:"+news.getNews()); } } /** * 關閉推送功能 * @param observable 被觀察者物件(這裡是新聞推送端) */ public void closePushMsg(Observable observable){ //從觀察者列表移除 observable.deleteObserver(this); } }

測試

package test;

public class Test  
{  
    public static void main(String[] args)  
    {  
        News news=new News();
        ClientX bObserver1=new ClientX();
        //客戶端開啟推送功能
        bObserver1.openPushMsg(news);
        news.setNews("世界末日了");
        //客戶端關閉推送功能
        bObserver1.closePushMsg(news);
        news.setNews("發現新大陸");

    }  
}

執行結果:

現在最新訊息:世界末日了

從執行結果上來看,只執行了開啟推送功能時的訊息推送。

java.util.Observable和java.util.Observer,的原始碼解析

  • 先看一下觀察者Observer介面的原始碼:
/*
 * Copyright (c) 1994, 1998, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.

 */
package java.util;

/**
 * A class can implement the <code>Observer</code> interface when it
 * wants to be informed of changes in observable objects.
 *
 * @author  Chris Warth
 * @see     java.util.Observable
 * @since   JDK1.0
 */
public interface Observer {
    /**
     * This method is called whenever the observed object is changed. An
     * application calls an <tt>Observable</tt> object's
     * <code>notifyObservers</code> method to have all the object's
     * observers notified of the change.
     *
     * @param   o     the observable object.
     * @param   arg   an argument passed to the <code>notifyObservers</code>
     *                 method.
     */
    void update(Observable o, Object arg);
}

開啟之後就一個update()的方法。每當被觀察者改變的時候,這個方法會得到執行。而且通過方法我們可以得到被觀察者物件和傳遞的一個Object物件。

  • Observable的原始碼
    程式碼雖然不多,也就不到一百行吧,但是註釋太多,先看結構圖吧:
    這裡寫圖片描述
    一個boolean changed變數用來判斷,被觀察者是否改變。
    一個Vector< Observer > obs集合物件,用來儲存註冊的觀察者。
  • addObserver
/**
     * Adds an observer to the set of observers for this object, provided
     * that it is not the same as some observer already in the set.
     * The order in which notifications will be delivered to multiple
     * observers is not specified. See the class comment.
     *
     * @param   o   an observer to be added.
     * @throws NullPointerException   if the parameter o is null.
     */
    public synchronized void addObserver(Observer o) {
        if (o == null)
            throw new NullPointerException();
        if (!obs.contains(o)) {
            obs.addElement(o);
        }
    }

如果傳遞進來的觀察者物件不為空,就新增進集合中

  • deleteObserver
 /**
     * Deletes an observer from the set of observers of this object.
     * Passing <CODE>null</CODE> to this method will have no effect.
     * @param   o   the observer to be deleted.
     */
    public synchronized void deleteObserver(Observer o) {
        obs.removeElement(o);
    }

有新增就有移除,移除觀察者物件。

  • notifyObservers無參
public void notifyObservers() {
        notifyObservers(null);
    }

無參的喚醒方法會呼叫有參的喚醒方法。

  • 列表內容
 public void notifyObservers(Object arg) {
        Object[] arrLocal;

        synchronized (this) {

            if (!changed)
                return;
            arrLocal = obs.toArray();
            clearChanged();
        }

        for (int i = arrLocal.length-1; i>=0; i--)
            ((Observer)arrLocal[i]).update(this, arg);
    }

如果被觀察者發生改變,就會把集合變成陣列,然後遍歷陣列,呼叫每個觀察者的update方法。

幾個主要的方法就這麼多,所有的被觀察者的註冊、取消註冊和喚醒觀察者的方法都是一樣的。java封裝成一個類就完成了方法的重複利用。如果採用面向介面的設計模式,那麼不同的被觀察者就必須去實現這些方法,而這些方法的邏輯是一樣的,就不能重複利用程式碼了。