觀察者模式(Observer Pattern)——讓你的對象知悉現狀

目錄

概述

     觀察者模式定義了對象之間的一對多依賴,這樣一來,當一個對象改變狀態時,它的所有依賴者都會受到通知並自動更新。這裏改變的對象被稱爲主題(Subject)(也叫可觀察者),它的依賴者稱爲觀察者(Observer)

觀察者模式類圖

觀察者模式類圖

需要注意的東西

  •      觀察者和主題之間是松耦合方式結合,即主題不知道觀察者的具體實現,只知道觀察者所實現的接口。
  •      使用此模式時,可以從主題推(push)和拉(push)數據。(下面將會介紹)
  •      有多個觀察者時,不能依賴特定的次序。
  •      java中有多種觀察者模式的實現(多種暫時沒找到),比較通用的是java.util.Observer(觀察者)和java.util.Observable(主題)。
         需要注意的是,java.util.Observer是一個接口,而java.util.Observable是一個繼承自Object的類。以下是jdk1.8中Observer和Observable的類圖
    這裏寫圖片描述

從主題推(push)和拉(push)數據

     通過上面的觀察者模式類圖可以知道,observer中的update方法主要是用於被主題用以更新的方法。推、拉數據時間上就是對update方法做控制。以下是推、拉數據的部分僞代碼



/*
@param sub 主題,爲了讓觀察者知道是哪一個主題通知它的 
@param arg 通知的內容,是notifyObservers()傳入的數據對象
/*
public void update(Subject sub,Object arg){……}


//從主題推數據
/*
@param arg 通知的內容
*/
public void notifyObservers(Object arg){
    ```
    foreach(observe:Observers){
        ```
        observer.update(this,arg);
        ```
    }
    ```
}
//從主題拉數據
setChanged(){
    changed = true;
}
public void notifyObservers(Object arg){
    ```
    if(changed){
    // notify 只會在changed == true的時候通知觀察者
        foreach(observe:Observers){
            ```
            observer.update(this,arg);
            ```
        }
        chaned=false;
    }
    ```
}

事實上在我看來推數據和拉數據最大的區別拉數據讓觀察者可以擁有從主題處選擇數據的權利,而推數據,則不考慮觀察者是否需要,一味地推送信息。

應用場景

場景說明

     以氣象監測爲例。其中包含三個部分是氣象站(獲取實際氣象數據的物理裝置)、WeatherData對象(追蹤來自氣象站的數據,並更新佈告板)和佈告板(顯示目前天氣狀況給用戶看)。以下將利用觀察者模式去利用weatherData對象去獲取數據並更新佈告板。

類圖設計

這裏寫圖片描述

代碼實現


package observer.api;

/**
 * <p>ClassName      Subject
 * <p>Description
 * <p>Author         ChongLou
 * <p>Version
 * <p>Date           2017/7/12 21:56
 */
public interface Subject {

   /**
     * 註冊觀察者
     * @param observer
     */
    public void registerObserver(Observer observer);

    /**
     * 移除觀察者
     * @param observer
     */
    public void removeObserver(Observer observer);

    /**
     * 通知觀察者
     */
    public void notifyObservers();
}
package observer.api;

/**
 * <p>ClassName      Observer
 * <p>Description   觀察者
 * <p>Author         ChongLou
 * <p>Version
 * <p>Date            2017/7/12 21:57
 */
public interface Observer {

    /**
     * 主題用以更新觀察者
     * @param temperature
     * @param humidity
     * @param pressure
     */
    public void update(float temperature,float humidity ,float pressure);
}

package observer.api;

/**
 * <p>ClassName      DisplayElement
 * <p>Description
 * <p>Author         ChongLou
 * <p>Version
 * <p>Date            2017/7/12 21:59
 */
public interface DisplayElement {
    //佈告板顯示
    public void display();
}
package observer.impl;

import observer.api.Observer;
import observer.api.Subject;

import java.util.ArrayList;
import java.util.Iterator;

/**
 * <p>ClassName      WeatherData
 * <p>Description
 * <p>Author         ChongLou
 * <p>Version
 * <p>Date            2017/7/12 22:06
 */
public class WeatherData implements Subject {

    private ArrayList<Observer> observerList;
    private float               temperature; //溫度
    private float               humidity; //溼度
    private float               pressure; //氣壓

    public WeatherData() {
        this.observerList = new ArrayList<Observer>();
    }

    public void registerObserver(Observer observer) {
        observerList.add( observer );
    }

    public void removeObserver(Observer observer) {
        int index = observerList.indexOf( observer );
        if (index >= 0) {
            observerList.remove( index );
        }
    }

    public void notifyObservers() {
        Iterator<Observer> iterator = observerList.iterator();
        while (iterator.hasNext()) {
            iterator.next().update( temperature, humidity, pressure );
        }
    }

    /**
     * 得到更新值之後,通知觀察者
     */
    public void measurementsChanged() {
        notifyObservers();
    }

    public void setMeasurements(float temperature, float humidity, float pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        measurementsChanged();
    }

}
package observer.impl;

import observer.api.DisplayElement;
import observer.api.Observer;
import observer.api.Subject;

/**
 * <p>ClassName      CurrentConditionsDisplay
 * <p>Description    採用推的方式更新數據
 * <p>Author         ChongLou
 * <p>Version
 * <p>Date           2017/7/12 22:08
 */
public class CurrentConditionsDisplay implements DisplayElement, Observer {

    private float temperature; //溫度
    private float humidity; //溼度
    private float pressure; //氣壓
    private Subject subject ; //當前通知的主題

    public CurrentConditionsDisplay(Subject subject) {
        this.subject = subject;
    }

    public void update(float temperature, float humidity, float pressure) {
        if (subject instanceof WeatherData) {
            // 如果是當前主題是WeatherData,纔會更新佈告板
            this.temperature = temperature;
            this.humidity = humidity;
            this.pressure = pressure;
            display();
        }
    }

    public void display() {
        System.out.println(
                "天氣信息更新啦!!!" +
                        "temperature=" + temperature +
                        ", humidity=" + humidity +
                        ", pressure=" + pressure );
    }


}
import observer.impl.CurrentConditionsDisplay;
import observer.impl.WeatherData;

/**
 * <p>ClassName      TestObserver
 * <p>Description     測試
 * <p>Author         ChongLou
 * <p>Version
 * <p>Date           2017/7/12 22:21
 */
public class TestObserver {

    public static void main(String[] args) {
        WeatherData weatherData = new WeatherData();
        CurrentConditionsDisplay currentConditionsDisplay1 = new CurrentConditionsDisplay( weatherData );
        CurrentConditionsDisplay currentConditionsDisplay2 = new CurrentConditionsDisplay( weatherData );

        weatherData.registerObserver( currentConditionsDisplay1 );
        weatherData.setMeasurements( 1.1f,1.2f,1.3f );
        weatherData.registerObserver( currentConditionsDisplay2 );
        weatherData.setMeasurements( 2.1f,2.2f,3.3f );
        weatherData.removeObserver( currentConditionsDisplay1 );
        weatherData.setMeasurements( 3.1f,3.2f,3.3f );
    }
}

運行結果
這裏寫圖片描述

總結

     第一次寫正式技術博客,參考了很多大牛寫設計模式學習筆記的邏輯。在寫博客時,會根據自己對書本的不瞭解,而去查各種資料,在查資料時,又會get很多東西。
     好好學習,天天向上。諸君,切記持之以恆!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章