摘要:三內(nèi)置觀察者模式了解內(nèi)置觀察者模式包內(nèi)包含最基本的類與接口,這和上面的接口與接口很類似。根據(jù)具體的需求,如果內(nèi)置的觀察者模式不能滿設(shè)計(jì),那么我們可以像剛開始那樣自己實(shí)現(xiàn)一套觀察者模式。參考資料設(shè)計(jì)模式
一、了解觀察者模式
1.1 什么是觀察者模式
觀察者模式定義了對(duì)象之間的一對(duì)多依賴,這樣一來,當(dāng)一個(gè)對(duì)象狀態(tài)改變時(shí),它的所有依賴者都會(huì)收到通知并自動(dòng)更新。
典型的問題比如報(bào)社,只要你是他們的訂戶,他們每次有新報(bào)紙出版時(shí),就會(huì)向你這送來,當(dāng)你不想要看報(bào)紙時(shí),取消訂閱,他們就不會(huì)再給你送報(bào)紙。
1.2 觀察者模式組成結(jié)構(gòu)
抽象主題 (Subject):抽象主題角色把所有觀察者對(duì)象保存在一個(gè)集合里,每個(gè)主題都可以有任意數(shù)量的觀察者,抽象主題提供一個(gè)接口,可以增加和刪除觀察者對(duì)象。
具體主題 (ConcreteSubject):該角色將有關(guān)狀態(tài)存入具體觀察者對(duì)象,在具體主題的內(nèi)部狀態(tài)發(fā)生改變時(shí),給所有注冊(cè)過的觀察者發(fā)送通知。
抽象觀察者 (Observer):是觀察者的抽象類,它定義了一個(gè)更新接口,使得在得到主題更改通知時(shí)更新自己。
具體觀察者 (ConcrereObserver):實(shí)現(xiàn)抽象觀察者定義的更新接口,以便在得到主題更改通知時(shí)更新自身的狀態(tài)。
1.3 觀察者模式 UML 圖解
二、觀察者模式具體應(yīng)用2.1 問題描述
氣象觀測(cè)站系統(tǒng):該系統(tǒng)中包含三部分,分別是氣象站 (獲取實(shí)際氣象數(shù)據(jù)的物理裝置)、WeatherData 對(duì)象 (追蹤氣象站的數(shù)據(jù),并更新布告板) 和布告板 (顯示天氣狀況給用戶看,布告板共有兩個(gè),分別顯示當(dāng)前的溫度以及對(duì)天氣進(jìn)行預(yù)告)。
2.2 問題分析
我們想要使用觀察者模式去解決這個(gè)問題,首先要分析出什么是主題,什么是觀察者,問題的關(guān)鍵是找出一對(duì)多依賴關(guān)系。這里 WeatherData 類正如所說的“一”,而“多”是用于顯示天氣情況的布告板。
WeatherData 是有狀態(tài)的對(duì)象,它包括了溫度、濕度和氣壓,而這些值都會(huì)變化,當(dāng)這些值改變時(shí),必須通知布告板,好讓它們顯示最新的數(shù)據(jù)。所以把 WeatherData 類作為主題,布告板作為觀察者。
2.3 問題分析設(shè)計(jì)圖
2.4 代碼實(shí)現(xiàn)
PS:代碼模塊較多,建議將這些代碼拷下來運(yùn)行一遍。
抽象主題接口 Subject
package com.jas.observer; public interface Subject { /** * 注冊(cè)觀察者 * * @param observer 觀察者對(duì)象 */ void registObserver(Observer observer); /** * 移除觀察者 * * @param observer 觀察者對(duì)象 */ void removeObserver(Observer observer); /** * 當(dāng)主題狀態(tài)改變時(shí),這個(gè)方法會(huì)被調(diào)用,通知所有的觀察者 */ void notifyObservers(); }
抽象觀察者接口 Observer
package com.jas.observer; public interface Observer { /** * 當(dāng)氣象觀測(cè)值改變時(shí),主題會(huì)把這些狀態(tài)值作為參數(shù),傳送給觀察者 * * @param temp 溫度 * @param humidity 濕度 * @param pressure 壓力 */ void update(float temp, float humidity, float pressure); }
布告信息接口 DisplayElement
package com.jas.observer; public interface DisplayElement { void display(); }
具體主題類 WeatherData
package com.jas.observer; import java.util.ArrayList; import java.util.List; public class WeatherData implements Subject { private float temperature; private float humidity; private float pressure; private Listlist = new ArrayList(); //使用集合保存所有的觀察者對(duì)象 @Override public void registObserver(Observer observer) { list.add(observer); } @Override public void removeObserver(Observer observer) { int i = list.indexOf(observer); if(i >= 0 && i < list.size()){ list.remove(i); } } @Override public void notifyObservers() { for (int i = 0; i < list.size(); i++) { //遍歷集合中所有觀察者對(duì)象 Observer observer = list.get(i); observer.update(temperature,humidity,pressure); //調(diào)用觀察者的 update() 方法 } } /** * 當(dāng)氣象站的數(shù)據(jù)得到更新后,通知觀察者,調(diào)用 notifyObservers() 方法 */ public void measurementsChanged(){ notifyObservers(); } /** * 當(dāng)氣象站數(shù)據(jù)改變后,設(shè)置新的數(shù)據(jù)值,并調(diào)用 measurementsChanged() 方法 * * @param temperature 溫度 * @param humidity 濕度 * @param pressure 氣壓 */ public void setMeasurements(float temperature, float humidity, float pressure){ this.temperature = temperature; this.humidity = humidity; this.pressure = pressure; measurementsChanged(); } }
具體觀察者對(duì)象,當(dāng)前天氣信息類 CurrentConditionsDisplay
package com.jas.observer; public class CurrentConditionsDisplay implements Observer, DisplayElement { private float temperature; private float humidity; private float pressure; private Subject weatherData; /** * 通過構(gòu)造函數(shù)將當(dāng)前觀察者注冊(cè)給具體主題對(duì)象 * * @param weatherData 主題對(duì)象 */ public CurrentConditionsDisplay(Subject weatherData){ this.weatherData = weatherData; weatherData.registObserver(this); } /** * 布告板信息展示 */ @Override public void display() { System.out.println("Current conditions list : " + "溫度 = " + temperature + ", 濕度 = " + humidity + ", 氣壓 = " + pressure); } /** * 更新信息 * * @param temp 溫度 * @param humidity 濕度 * @param pressure 壓力 */ @Override public void update(float temp, float humidity, float pressure) { this.temperature = temp; this.humidity = humidity; this.pressure = pressure; display(); } }
具體觀察者對(duì)象,預(yù)測(cè)天氣信息類 ForecastDisplay(簡(jiǎn)單將數(shù)據(jù)減一)
package com.jas.observer; public class ForecastDisplay implements Observer, DisplayElement { private float temperature; private float humidity; private float pressure; private Subject weatherData; /** * 通過構(gòu)造函數(shù)將當(dāng)前觀察者注冊(cè)給主題對(duì)象 * * @param weatherData 主題對(duì)象 */ public ForecastDisplay(WeatherData weatherData){ this.weatherData = weatherData; weatherData.registObserver(this); } @Override public void display() { System.out.println("Forecast conditions list : " + "溫度 = " + (temperature - 1.0) + ", 濕度 = " + (humidity - 1.0) + ", 氣壓 = " + (pressure - 1.0)); } @Override public void update(float temp, float humidity, float pressure) { this.temperature = temp; this.humidity = humidity; this.pressure = pressure; display(); } }
氣象站類 WeatherStation
package com.jas.observer; public class WeatherStation { public static void main(String[] args) { WeatherData weatherData = new WeatherData(); CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData); ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData); //當(dāng)具體主題對(duì)象數(shù)據(jù)發(fā)生變化,所有依賴者 (觀察者) 都會(huì)實(shí)現(xiàn)自動(dòng)數(shù)據(jù)更新 weatherData.setMeasurements(18,65,30); } } /** * 輸出 * Current conditions list : 溫度 = 18.0, 濕度 = 65.0, 氣壓 = 30.0 * Forecast conditions list : 溫度 = 17.0, 濕度 = 64.0, 氣壓 = 29.0 */
2.5 自定義觀察者模式總結(jié)
觀察者模式可以輕松實(shí)現(xiàn)松耦合,因?yàn)橹黝}并不需要知道觀察者的具體類是誰,做了些什么,并且我們可以在任何時(shí)候新增觀察者。由于一個(gè)主題可能對(duì)應(yīng)多個(gè)觀察者,所以當(dāng)某一個(gè)觀察者出現(xiàn)問題時(shí),可能導(dǎo)致其他的觀察者也不能正常工作。因此在一定程度上,存在著效率問題。
三、Java 內(nèi)置觀察者模式3.1 了解 Java 內(nèi)置觀察者模式
java.util 包內(nèi)包含最基本的Observable 類與 Observer 接口,這和上面的 Subject 接口與 Observer 接口很類似。Observable 類與 Observer 接口使用起來更方便,因?yàn)樵S多的功能已經(jīng)提供了。
3.2 Java 內(nèi)置觀察者模式如何運(yùn)作
(1)如何把對(duì)象定義為觀察者?
實(shí)現(xiàn)觀察者 (Observer) 接口,調(diào)用任何 Observable 對(duì)象的 addObserve() 方法。當(dāng)不想要當(dāng)觀察者時(shí),調(diào)用 deleteObserve() 方法。
(2)可觀察者如何發(fā)送通知?
先調(diào)用 setChanged() 方法,標(biāo)記狀態(tài)已經(jīng)被改變的事實(shí)。
調(diào)用 notifyObservers() 或 notifyObservers(Object arg) 方法。
(3)觀察者如何接收通知?
同以前一樣,觀察者實(shí)現(xiàn)了 update(Observable o, Object arg) 方法,只是方法簽名不太一樣。
3.3 重寫氣象觀測(cè)站系統(tǒng)
主題類 WeatherData
package com.jas.jdk.observer; import java.util.Observable; public class WeatherData extends Observable { private float temperature; private float humidity; private float pressure; public void measurementsChanged(){ //在通知所有觀察者之前,先調(diào)用 setChanged() 方法,用來表示狀態(tài)已經(jīng)改變 setChanged(); notifyObservers(); } public void setMeasurements(float temperature, float humidity, float pressure){ this.temperature = temperature; this.humidity = humidity; this.pressure = pressure; measurementsChanged(); } public float getTemperature(){ return temperature; } public float getHumidity(){ return humidity; } public float getPressure() { return pressure; } }
具體觀察者對(duì)象,當(dāng)前天氣信息類 CurrentConditionsDisplay
package com.jas.jdk.observer; import java.util.Observable; import java.util.Observer; public class CurrentConditionsDisplay implements Observer, DisplayElement { private float temperature; private float humidity; private float pressure; private Observable observable; /** * 通過構(gòu)造函數(shù),將當(dāng)前對(duì)象記錄為觀察者 * * @param observable 主題對(duì)象 */ public CurrentConditionsDisplay(Observable observable){ this.observable = observable; observable.addObserver(this); } @Override public void display() { System.out.println("Current conditions list : " + "溫度 = " + temperature + ", 濕度 = " + humidity + ", 氣壓 = " + pressure); } @Override public void update(Observable o, Object arg) { if(o instanceof WeatherData){ WeatherData weatherData = (WeatherData) o; this.temperature = weatherData.getTemperature(); this.humidity = weatherData.getHumidity(); this.pressure = weatherData.getPressure(); display(); } } }
氣象站類 WeatherStation(同上)
package com.jas.jdk.observer; public class WeatherStation { public static void main(String[] args) { WeatherData weatherData = new WeatherData(); CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData); //ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData); weatherData.setMeasurements(80,65,30.4f); } } /** * 輸出 * Current conditions list : 溫度 = 80.0, 濕度 = 65.0, 氣壓 = 30.4 */
3.4 Java 內(nèi)置觀察者模式總結(jié)
Java 內(nèi)置的觀察者模式允許觀察者有選擇的獲取數(shù)據(jù),而不是主題對(duì)象強(qiáng)制將更新數(shù)據(jù)全部推送個(gè)每個(gè)觀察者。
Observable 是一個(gè)類,并不是一個(gè)接口,這意味著你繼承它的同時(shí),不能再繼承其他的類。在 Observable 類中 setChanged() 方法被保護(hù)了起來 (protected),除非你繼承該類,否則你無法創(chuàng)建 Observable 實(shí)例組合到你自己的對(duì)象中來。所以它違反了一個(gè)原則:“多用組合,少用繼承”。
還有一點(diǎn)需要要注意的是:內(nèi)置的觀察者模式,觀察者被通知的順序并不是唯一的 (上面只定義了一個(gè)觀察者),有時(shí)候并不能達(dá)到我們一開始的目的,你可以定義多個(gè)觀察者驗(yàn)證一下。
根據(jù)具體的需求,如果 Java 內(nèi)置的觀察者模式 API 不能滿設(shè)計(jì),那么我們可以像剛開始那樣自己實(shí)現(xiàn)一套觀察者模式。
參考資料《Head First 設(shè)計(jì)模式》
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/77238.html
摘要:觀察者模式的使用場(chǎng)景比如你微博關(guān)注了一個(gè)人,那么這個(gè)人發(fā)布的微博就會(huì)推送到你這。 Java設(shè)計(jì)模式之觀察者模式 一直想寫一篇學(xué)習(xí)觀察者模式的總結(jié)沒有契機(jī),今天學(xué)習(xí)阻塞隊(duì)列的原理時(shí)候看到在實(shí)現(xiàn)生產(chǎn)者消費(fèi)者的時(shí)候用到了通知模式,就是所謂的觀察者模式,正好順便整理一下。 1. 簡(jiǎn)介 觀察者模式定義對(duì)象間的一種一對(duì)多的依賴關(guān)系,當(dāng)一個(gè)對(duì)象的狀態(tài)發(fā)生改變時(shí),所有依賴于它的對(duì)象都得到通知并被自動(dòng)更...
摘要:觀察者模式也稱發(fā)布訂閱模式它的作用就是當(dāng)一個(gè)對(duì)象的狀態(tài)發(fā)生改變時(shí),所有依賴于它的對(duì)象都將得到通知,自動(dòng)刷新對(duì)象狀態(tài)舉個(gè)生活比較常見常見的例子比如你去面試之后,面試官看你表現(xiàn)不錯(cuò),最后會(huì)跟你要聯(lián)系方式,以便之后可以聯(lián)系你。 觀察者模式也稱發(fā)布-訂閱模式,它的作用就是當(dāng)一個(gè)對(duì)象的狀態(tài)發(fā)生改變時(shí),所有依賴于它的對(duì)象都將得到通知,自動(dòng)刷新對(duì)象狀態(tài) 舉個(gè)生活比較常見常見的例子,比如你去面試之后,...
摘要:關(guān)鍵概念理解觀察者設(shè)計(jì)模式中主要區(qū)分兩個(gè)概念觀察者指觀察者對(duì)象,也就是消息的訂閱者被觀察者指要觀察的目標(biāo)對(duì)象,也就是消息的發(fā)布者。 原文首發(fā)于微信公眾號(hào):jzman-blog,歡迎關(guān)注交流! 最近補(bǔ)一下設(shè)計(jì)模式相關(guān)的知識(shí),關(guān)于觀察者設(shè)計(jì)模式主要從以下幾個(gè)方面來學(xué)習(xí),具體如下: 什么是觀察者設(shè)計(jì)模式 關(guān)鍵概念理解 通知觀察者的方式 觀察者模式的實(shí)現(xiàn) 觀察者模式的優(yōu)缺點(diǎn) 使用場(chǎng)景 下面...
本文從jdk內(nèi)置的觀察者模式來介紹觀察者模式。業(yè)務(wù)場(chǎng)景:當(dāng)老師進(jìn)門的時(shí)候,班長(zhǎng)帶頭叫老師好,然后全班同學(xué)一起交老師好因?yàn)樘?jiǎn)單,直接上代碼 班長(zhǎng)繼承自O(shè)bservable package Observer; import java.util.Observable; import java.util.Observer; public class Monitor extends Observabl...
摘要:觀察者模式,是一對(duì)多的關(guān)系,一個(gè)主題對(duì)應(yīng)多個(gè)觀察者,當(dāng)這個(gè)主題發(fā)生變化的時(shí)候,所有觀察著這個(gè)主題的觀察者都會(huì)接收到通知來獲悉主題的變化。這就是使用的觀察者模式,下面就讓我們用代碼實(shí)現(xiàn)觀察者模式。 觀察者模式,是一對(duì)多的關(guān)系,一個(gè)主題對(duì)應(yīng)多個(gè)觀察者,當(dāng)這個(gè)主題發(fā)生變化的時(shí)候,所有觀察著這個(gè)主題的觀察者都會(huì)接收到通知來獲悉主題的變化。 在現(xiàn)實(shí)中我們也會(huì)遇到許許多多應(yīng)用觀察者模式的行為,比如...
閱讀 2696·2021-10-14 09:43
閱讀 3639·2021-10-13 09:39
閱讀 3354·2019-08-30 15:44
閱讀 3207·2019-08-29 16:37
閱讀 3781·2019-08-29 13:17
閱讀 2789·2019-08-26 13:57
閱讀 1895·2019-08-26 11:59
閱讀 1347·2019-08-26 11:46