1. 程式人生 > >Java設計模式知識學習-----觀察者模式

Java設計模式知識學習-----觀察者模式

定義

出版社+訂閱者=觀察者模式,其中出版社改成為‘主題’,訂閱者改稱為‘觀察者’。觀察者模式定義了物件之間的一對多依賴關係,當一個物件改變狀態時,他的所有依賴者都會收到通知並且自動更新。這裡和訂閱報紙是一樣的道理,當出版社有了新報紙,那麼所有訂閱了該報紙的人都會收到新的報紙。
在這裡,發生改變的物件(出版社)稱為觀察目標(主題),被通知的物件(訂閱報紙的人)稱為觀察者,一個觀察目標可以對應多個觀察者,而這些觀察者之間沒有相互聯絡,所以可以根據需要增加和刪除觀察者,使得系統更易於擴充套件。

類圖

例項

需求

在氣象觀測站中,有三個佈告板,分別顯示目前狀況,氣象統計和天氣預報。WeatherData類中具有setter和getter方法,可以取得三個測量值,當有新的資料時,WeatherData類中的measurementsChanged()方法就會被呼叫,我們要做的就是,一旦WeatherData類有新資料,我們就要更新佈告板。

類圖設計

程式碼實現

Subject 主題介面

public interface Subject {
    //觀察者註冊
    public void registerObserver(Observer o);
    //移除觀察者
    public void removeObserver(Observer o);
    //當主題狀態該變時,這個方法就會被呼叫,以通知所有的觀察者
    public void notifyObserver();
}

Observer 觀察者介面

public interface Observer {
    //當氣象觀測值改變時,主題就會將這些狀態值當做方法的引數,傳送給觀察者
    //所有的的觀察者都必須實現update方法,以實現觀察者介面
    public void update(float temperature,float humidity,float pressure);
}

DisplayElement 佈告板顯示介面

public interface DisplayElement {
    //當佈告板需要顯示的,就會呼叫此方法
    public void display();
}

WeatherData實現主題介面 WeatherData.java

public class WeatherData implements Subject {
    private ArrayList<Observer> observers;
    private float temperature;
    private float humidity;
    private float pressure;
    //使用一個集合來記錄觀察值
    public WeatherData() {
        observers = new ArrayList<Observer>();
    }
    //註冊成為觀察者
    @Override
    public void registerObserver(Observer o) {
        observers.add(o);

    }

    @Override
    public void removeObserver(Observer o) {
        int i = observers.indexOf(o);
        if(i >=0){
            observers.remove(o);
        }
    }
    //這裡就是將狀態迴圈的通知給每一個觀察者,因為每一個觀察者都實現了update方法,
    @Override
    public void notifyObserver() {
        for(int i=0; i < observers.size();i++){
            Observer observer = observers.get(i);
            observer.update(temperature, humidity, pressure);
        }
    }
    //當氣象站得到更新資料中,就通知觀察者
    public void measurementsChanged(){
        notifyObserver();
    }
    public void setMeasurements(float temperature,float humidity, float pressure){
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        measurementsChanged();

    }
}

CurrentConditionsDisplay 佈告板

/**
 * 顯示目前狀況的佈告板:顯示溫度,溼度和氣壓
 *@author lky
 *@date 2018年1月16日
 */
public class CurrentConditionsDisplay implements Observer,DisplayElement {

    private float temperature;// 溫度
    private float humidity;// 溼度
    private float pressure; // 氣壓
    private Subject weatherData;
    public CurrentConditionsDisplay(Subject weatherData) {
        this.weatherData = weatherData;
        weatherData.registerObserver(this);
    }
    @Override
    public void display() {
        System.out.println("我是目前狀況佈告板,現在溫度是:"+temperature+
                "℃"+"溼度是:"+humidity+"氣壓是:"+pressure);
    }

    @Override
    public void update(float temperature, float humidity, float pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        display();
    }

}

天氣預報佈告板

/**
 * 天氣預報佈告板:顯示當前的天氣狀況
 *@author lky
 *@date 2018年1月16日
 */
public class ForecastDisplay implements Observer,DisplayElement{
    private float currentPressure = 29.92f;  //當前的氣壓
    private float lastPressure; // 最後的氣壓
    private WeatherData weatherData;

    public ForecastDisplay(WeatherData weatherData) {
        this.weatherData = weatherData;
        weatherData.registerObserver(this);
    }

    public void update(float temperature,float humidity,float pressure) {
        lastPressure = currentPressure;// 將當前的氣壓賦給最後的氣壓
        currentPressure = pressure;// 將更新的氣壓賦給當前氣壓
        display();
    }

    public void display() {

        if (currentPressure > lastPressure) {
            System.out.println("我是天氣預報佈告板,現在是:氣壓升高,天氣轉好!");
        } else if (currentPressure == lastPressure) {
            System.out.println("我是天氣預報佈告板,現在是:氣壓不變,天氣維持!");
        } else if (currentPressure < lastPressure) {
            System.out.println("我是天氣預報佈告板,現在是:氣壓降低,天氣變壞!");
        }
    }

}

天氣統計佈告板

/**
 * 天氣統計佈告板:顯示最高溫度,最低溫度和平均溫度
 *@author lky
 *@date 2018年1月16日
 */
public class StatisticsDisplay implements Observer,DisplayElement{
    private float maxTemp = 0.0f;
    private float minTemp = 200;
    private float tempSum= 0.0f;
    private int numReadings;
    private WeatherData weatherData;

    public StatisticsDisplay(WeatherData weatherData) {
        this.weatherData = weatherData;
        weatherData.registerObserver(this);
    }

    public void update(float temperature, float humidity, float pressure) {
        tempSum += temperature;
        numReadings++;

        if (temperature > maxTemp) {
            maxTemp = temperature;
        }

        if (temperature < minTemp) {
            minTemp = temperature;
        }

        display();
    }

    public void display() {
        System.out.println("我是天氣統計佈告板,平均溫度是: " + (tempSum / numReadings)
            + ",最高溫度是:" + maxTemp + ",最低溫度是:" + minTemp);
    }

}

酷熱指數佈告板

/**
 * 酷熱指數佈告板,根據溫度和溼度採用特定的公式映象計算的。
 *@author lky
 *@date 2018年1月16日
 */
public class HeatIndexDisplay implements Observer,DisplayElement {
    float heatIndex = 0.0f;
    private WeatherData weatherData;

    public HeatIndexDisplay(WeatherData weatherData) {
        this.weatherData = weatherData;
        weatherData.registerObserver(this);
    }

    public void update(float temperature,float humidity,float pressure) {
        heatIndex = computeHeatIndex(temperature, humidity);
        display();
    }
    //在佈告板中自定義方法。
    private float computeHeatIndex(float t, float rh) {
        float index = (float)((16.923 + (0.185212 * t) + (5.37941 * rh) - (0.100254 * t * rh) 
            + (0.00941695 * (t * t)) + (0.00728898 * (rh * rh)) 
            + (0.000345372 * (t * t * rh)) - (0.000814971 * (t * rh * rh)) +
            (0.0000102102 * (t * t * rh * rh)) - (0.000038646 * (t * t * t)) + (0.0000291583 * 
            (rh * rh * rh)) + (0.00000142721 * (t * t * t * rh)) + 
            (0.000000197483 * (t * rh * rh * rh)) - (0.0000000218429 * (t * t * t * rh * rh)) +
            0.000000000843296 * (t * t * rh * rh * rh)) -
            (0.0000000000481975 * (t * t * t * rh * rh * rh)));
        return index;
    }

    public void display() {
        System.out.println("我是酷熱指數佈告板,現在的酷熱指數為: " + heatIndex);
    }
}

測試

public class WeatherStation {
    public static void main(String[] args) {
        WeatherData weatherData = new WeatherData();
        CurrentConditionsDisplay conditionsDisplay = new CurrentConditionsDisplay(weatherData);
        StatisticsDisplay observer2 = new StatisticsDisplay(weatherData);
        ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData);
        HeatIndexDisplay heatIndexDisplay = new HeatIndexDisplay(weatherData);
        weatherData.setMeasurements(80, 65, 30.4f);
        System.out.println("===================================");
        weatherData.setMeasurements(70, 55, 20.4f);
        System.out.println("===================================");
        weatherData.setMeasurements(90, 75, 40.4f);

    }
}

結果

我是目前狀況佈告板,現在溫度是:80.0℃溼度是:65.0氣壓是:30.4
我是天氣統計佈告板,平均溫度是: 80.0,最高溫度是:80.0,最低溫度是:80.0
我是天氣預報佈告板,現在是:氣壓升高,天氣轉好!
我是酷熱指數佈告板,現在的酷熱指數為: 82.95535
===================================
我是目前狀況佈告板,現在溫度是:70.0℃溼度是:55.0氣壓是:20.4
我是天氣統計佈告板,平均溫度是: 75.0,最高溫度是:80.0,最低溫度是:70.0
我是天氣預報佈告板,現在是:氣壓降低,天氣變壞!
我是酷熱指數佈告板,現在的酷熱指數為: 75.9113
===================================
我是目前狀況佈告板,現在溫度是:90.0℃溼度是:75.0氣壓是:40.4
我是天氣統計佈告板,平均溫度是: 80.0,最高溫度是:90.0,最低溫度是:70.0
我是天氣預報佈告板,現在是:氣壓升高,天氣轉好!
我是酷熱指數佈告板,現在的酷熱指數為: 108.19608

執行流程分析

  1. 例項化一個主題物件(WeatherData)
  2. 例項化一個現實目前狀況的佈告板,並傳入主題物件。
    • 在目前狀況的佈告板的建構函式中,將當前佈告板註冊為傳入的主題的觀察者
    • 同理,將天氣統計佈告板,天氣預報佈告板,酷熱指數佈告板都註冊為WeatherData的觀察者
  3. 呼叫主題物件的setMeasurements方法,模擬主題物件的值發生改變。
  4. 第一次傳入不同的溫度,溼度和氣壓。主題物件呼叫measurementsChanged()方法。
  5. measurementsChanged()方法中呼叫notifyObserver()方法。
  6. notifyObserver()方法中迴圈呼叫observer的update()方法,呼叫的是介面方法,具體執行的是每一個例項型別的update()方法,
  7. 每一個佈告板例項的update()方法,是具體的資料準備,然後呼叫display()方法,顯示佈告板資訊。

整體分析圖

總結

  1. 觀察者模式定義了物件之間的一對多關係,多個觀察者監聽同一個主題(被觀察者),當主題的狀態發生改變時,會通知所有的觀察者。
  2. 觀察者模式中具體主題是主題的子類,通常包含經常發生改變的資料,當他的狀態發生改變時,會通知所有他的觀察者物件,主題用一個共同的介面來更新觀察者。
  3. 觀察者實現的是同一個介面,也就是主題發生改變時通知的介面,不同的觀察者具體的實現方法不一樣。
  4. 觀察者與被觀察者之間用鬆耦合的方式結合。

相關推薦

Java設計模式知識學習-----觀察模式

定義 出版社+訂閱者=觀察者模式,其中出版社改成為‘主題’,訂閱者改稱為‘觀察者’。觀察者模式定義了物件之間的一對多依賴關係,當一個物件改變狀態時,他的所有依賴者都會收到通知並且自動更新。這裡和訂閱報紙是一樣的道理,當出版社有了新報紙,那麼所有訂閱了該報紙的人

Java設計模式11:觀察模式

一、什麼是觀察者模式 在閻巨集博士的《JAVA與模式》一書中開頭是這樣描述觀察者(Observer)模式的:   觀察者模式是物件的行為模式,又叫釋出-訂閱(Publish/Subscribe)模式、模型-檢視(Model/View)模式、源-監聽器(Source/Listener)模式

設計模式學習——觀察模式

觀察者模式 定義:定義物件間一種一對多的依賴關係,使得每當一個物件發生變化時,則所有依賴於他的物件都會得到通知並自動更新。 使用場景 關聯行為場景 事件多級觸發場景 跨系統的訊息交換場景,如訊息佇列、事件匯流排的處理機制 特點:實現物件的解耦,將觀察

Java設計模式之《觀察模式》及應用場景

觀察者模式,又可以稱之為釋出-訂閱模式,觀察者,顧名思義,就是一個監聽者,類似監聽器的存在,一旦被觀察/監聽的目標發生的情況,就會被監聽者發現,這麼想來目標發生情況到觀察者知道情況,其實是由目標獎情況傳送到觀察者的。 觀察者模式多用於實現訂閱功

Java設計模式六:觀察模式(Observer)

觀察者模式定義了物件間的一種一對多依賴關係,使得每當一個物件改變狀態,則所有依賴於它的物件都會得到通知並被自動更新。 它將觀察者和被觀察者的物件分離開。提高了應用程式的可維護性和重用性。 實現觀察者模式有很多形式,一種是“註冊---通知---撤銷註冊”的形式。 觀察者Observer:所有潛在的觀察

Java描述設計模式(11):觀察模式

本文原始碼:GitHub·點這裡 || GitEE·點這裡 一、觀察者模式 1、概念描述 觀察者模式是物件的行為模式,又叫釋出

重學 Java 設計模式:實戰觀察模式「模擬類似小客車指標搖號過程,監聽訊息通知使用者中籤場景」

![](https://img-blog.csdnimg.cn/20200630231649444.png) 作者:小傅哥 部落格:[https://bugstack.cn](https://bugstack.cn) - `原創系列專題文章` >沉澱、分享、成長,讓自己和他人都能有所收穫!

設計思想與模式之五觀察模式

per -- 什麽 over out junit 測試類 rem second 一 概述 1.什麽是觀察者模式? 將一個對象視作被觀察者,當它發出某種行為或者發生某種變化時,通知所有依賴它的對象(觀察者),這種設計模式叫做觀察者設計模式。 2.觀察者設

設計模式筆記8: 觀察模式

image 子類 http view 完全 log 註冊 功能 技術分享 1.1  定義   定義了一種一對多的依賴關系,讓多個觀察者同時監聽一個對象,但這個對象發生變化時,會通知所有觀察者對象,使他們能夠更新自己。 1.2  類圖 1.3  代碼 Vi

Android 設計模式情景分析——觀察模式

get ble not exception 知識庫 system observe 行為型模式 .get 觀察者模式是一種使用頻率非常高的模式,有時也被稱作發布/訂閱模式,屬於行為型模式,它最常用的是 GUI 系統、訂閱——發布系統,它一個重要作用就是解耦,使得它們之間的依賴

淺談java中內置的觀察模式與動態代理的實現

所有 代理 notify play ani effect 一個 indicate protected 一.關於觀察者模式 1.將觀察者與被觀察者分離開來,當被觀察者發生變化時,將通知所有觀察者,觀察者會根據這些變化做出對應的處理。 2.jdk裏已經提供對應的Observer

設計模式模式PK:觀察模式VS責任鏈模式

ipa 隨機 保留 聲明 pri 測試 void c_str window 1、概述 為什麽要把觀察者模式和責任鏈模式放在一起對比呢?看起來這兩個模式沒有太多的相似性,真沒有嗎?回答是有。我們在觀察者模式中也提到了觸發鏈(也叫做觀察者鏈)的問題,一個具體的角色既可以是觀察者

設計模式(19)--Observer(觀察模式)--行為型

直接 pen 創建 方法調用 設計方案 之間 分離 number 運行期 作者QQ:1095737364 QQ群:123300273 歡迎加入! 1.模式定義:   觀察者模式是對象的行為模式,又叫發布-訂閱(Publish/Subscribe)模式、模

【編程思想】【設計模式】【行為模式Behavioral】觀察模式Observer

setting notify tput pes env observer 設計模式 mod pre Python轉載版 https://github.com/faif/python-patterns/blob/master/behavioral/observer.py

【Python設計模式】06 觀察模式-瞭解物件的情況

六、觀察者模式-瞭解物件的情況 這章可以討論行為型設計模式:觀察者設計模式 本章主題 行為型設計模式簡介 觀察者設計模式及其 UML圖 利用 Python3.x程式碼實現一個真實用例 鬆耦合的強大威力 常見問答 1. 行為型模式簡介

vue原始碼學習——觀察模式

情景:接觸過vue的同學都知道,我們曾經都很好奇為什麼vue能這麼方便的進行資料處理,當一個物件的某個狀態改變之後,只要依賴這個資料顯示的部分也會發生改變,如果你依舊很好奇,那麼今天你就可以瞭解一下實現的原理 什麼是觀察者模式​​​​​​​    官方解釋是

設計模式(十六) 觀察模式

觀察者模式也叫釋出/訂閱模式, 定義一種1對多的依賴關係,使得每當一個物件改變狀態,則所有依賴於它的物件都會得到通知並被自動更新 觀察者模式的幾個角色: Subject 被觀察者。定義被觀察者必須實現的職責,它必須能夠動態的增加,取消觀察者。 要有管理觀察者並通知觀

設計模式 ( 十五 ) 觀察模式Observer(物件行為型)

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

設計模式——行為型——觀察模式

在閻巨集博士的《JAVA與模式》一書中開頭是這樣描述觀察者(Observer)模式的:   觀察者模式是物件的行為模式,又叫釋出-訂閱(Publish/Subscribe)模式、模型-檢視(Model/View)模式、源-監聽器(Source/Listener)模式或從屬者(Dependents

設計模式(DesignPattern)之觀察模式(Observable)

1、定義   觀察者模式的重要作用就是解耦,定義物件之間一對多的依賴關係,被依賴物件為Subject,依賴物件為Observer,Subject通知Observer更新狀態。 2、使用場景 跨系統的訊息交換,如訊息佇列,事件匯流排的處理機制。 事件多級觸發。 關聯的