Java設(shè)計(jì)模式之觀察者模式
一、觀察者模式的定義和特點(diǎn)
觀察者模式的定義:
指多個(gè)對(duì)象間存在一對(duì)多的依賴關(guān)系,當(dāng)一個(gè)對(duì)象的狀態(tài)發(fā)生改變時(shí),所有依賴于它的對(duì)象都得到通知并被自動(dòng)更新。這種模式有時(shí)又稱作發(fā)布-訂閱模式、模型-視圖模式,它是對(duì)象行為型模式。
特點(diǎn):
降低了目標(biāo)與觀察者之間的耦合關(guān)系,兩者之間是抽象耦合關(guān)系。符合依賴倒置原則。
目標(biāo)與觀察者之間建立了一套觸發(fā)機(jī)制。
二、觀察者模式的結(jié)構(gòu)
實(shí)現(xiàn)觀察者模式時(shí)要注意具體目標(biāo)對(duì)象和具體觀察者對(duì)象之間不能直接調(diào)用,否則將使兩者之間緊密耦合起來,這違反了面向?qū)ο蟮脑O(shè)計(jì)原則。 觀察者模式的主要角色如下。
Subject
類:他把所有對(duì)觀察者對(duì)象的引用保存在一個(gè)聚合里,每個(gè)主題都可以有任何數(shù)量的觀察者,抽象主題提供一個(gè)接口,可以增加和刪除任意的觀察者對(duì)象observer
類:抽象觀察者,為所有的具體觀察者定義一個(gè)接口,在得到主題的通知時(shí)更新自己ConcreteSubject
:具體主題,將有關(guān)狀態(tài)存入具體觀察者對(duì)象,在具體主題的內(nèi)部狀態(tài)改變時(shí),給所有登記過的的觀察者發(fā)出通知ConcreteObserver
:具體觀察者,實(shí)現(xiàn)抽象觀察者角色所要求的的更新接口,以便使本身的狀態(tài)與主題的狀態(tài)向協(xié)調(diào)
三、代碼實(shí)例
現(xiàn)在有一個(gè)需求,各網(wǎng)站需要訂閱天氣需求, 我們這邊要及時(shí)更新并發(fā)送天氣信息,且我們可以自由的注冊(cè)或者移除想要發(fā)送的網(wǎng)站,用觀察者模式實(shí)現(xiàn)。
如果我們用傳統(tǒng)的模式實(shí)現(xiàn)該案例,那么會(huì)出現(xiàn)一個(gè)問題,就是如果我們要修改網(wǎng)站,那可能回去改動(dòng)網(wǎng)站類的代碼,和我們操作更新數(shù)據(jù)的代碼,這不符合我們的開閉原則,因此我們采用觀察者模式去實(shí)現(xiàn),因?yàn)樗彩且环N一對(duì)多的依賴關(guān)系,生活中這種案例多不勝數(shù),例如訂閱雜志,等。
結(jié)構(gòu)圖如下
代碼示例
抽象目標(biāo)類Subject
package com.observerPattern.weatherCase; /** * @author wang * @version 1.0 * @packageName com.observerPattern.weatherCase * @className Subject * @date 2021/12/28 15:49 * @Description Subject抽象目標(biāo)類,由具體的目標(biāo)去實(shí)現(xiàn) */ public interface Subject { /** * @Date 2021/12/28 16:20 * @Param * @param o * @Return void * @MetodName registerObserver * @Author wang * @Description 注冊(cè)觀察者方法 */ void registerObserver(Observer o); /** * @Date 2021/12/28 16:20 * @Param * @param o * @Return void * @MetodName removeObserver * @Author wang * @Description 移除觀察者 */ void removeObserver(Observer o); /** * @Date 2021/12/28 16:20 * @Param * @Return void * @MetodName notifyObservers * @Author wang * @Description 通知觀察者 */ void notifyObservers(); }
具體目標(biāo)WeatherDate類
package com.observerPattern.weatherCase; import java.util.ArrayList; /** * @author wang * @version 1.0 * @packageName com.observerPattern.weatherCase * @className WeatherDate * @date 2021/12/28 16:00 * @Description 包含最新的天氣數(shù)據(jù),是具體的目標(biāo),實(shí)現(xiàn)了抽象目標(biāo)subject * 該類含有觀察者集合,使用ArrayLis集合管理. * 當(dāng)數(shù)據(jù)有更新時(shí),就主動(dòng)的調(diào)用ArrayList集合通知各個(gè)觀察者 * */ public class WeatherDate implements Subject{ private float temperature; private float pressure; private float humidity; private ArrayList<Observer> observers; /** * @Date 2021/12/28 16:10 * @Param * @Return null * @MetodName WeatherDate * @Author wang * @Description 初始化觀察者集合 */ public WeatherDate() { this.observers = new ArrayList<Observer>(); } public float getTemperature() { return temperature; } public float getPressure() { return pressure; } public float getHumidity() { return humidity; } /** * @Date 2021/12/28 16:10 * @Param * @Return void * @MetodName dateChange * @Author wang * @Description 調(diào)用通知方法,將更新后的數(shù)據(jù)推送至各個(gè)觀察者 */ public void dateChange() { notifyObservers(); } /** * @Date 2021/12/28 16:11 * @Param * @param temperature * @param pressure * @param humidity * @Return void * @MetodName setDate * @Author wang * @Description 更新數(shù)據(jù) */ public void setDate(float temperature,float pressure,float humidity) { this.temperature = temperature; this.pressure = pressure; this.humidity = humidity; dateChange(); } /** * @Date 2021/12/28 16:11 * @Param * @param o * @Return void * @MetodName registerObserver * @Author wang * @Description z注冊(cè)一個(gè)觀察者 */ @Override public void registerObserver(Observer o) { observers.add(o); } /** * @Date 2021/12/28 16:11 * @Param * @param o * @Return void * @MetodName removeObserver * @Author wang * @Description 移除一個(gè)觀察者 */ @Override public void removeObserver(Observer o) { if(observers.contains(o)) { observers.remove(o); } } /** * @Date 2021/12/28 16:12 * @Param * @Return void * @MetodName notifyObservers * @Author wang * @Description 通知觀察者 */ @Override public void notifyObservers() { for(int i = 0;i< observers.size();i++) { observers.get(i).update(this.temperature,this.pressure,this.humidity); } } }
抽象觀察者Observer:
package com.observerPattern.weatherCase; /** * @author wang * @version 1.0 * @packageName com.observerPattern.weatherCase * @className Observer * @date 2021/12/28 15:50 * @Description 觀察者接口,方法更新溫度,壓力,濕度,由具體的觀察者實(shí)現(xiàn) */ public interface Observer { /** * @Date 2021/12/28 16:18 * @Param * @param temperature * @param pressure * @param humidity * @Return void * @MetodName update * @Author wang * @Description */ void update(float temperature,float pressure,float humidity); }
具體觀察者1
package com.observerPattern.weatherCase; /** * @author wang * @version 1.0 * @packageName com.observerPattern.weatherCase * @className CurrentCondition * @date 2021/12/28 15:54 * @Description 具體的一個(gè)觀察者類,表示當(dāng)前天氣情況,實(shí)現(xiàn)觀察者接口 */ public class CurrentCondition implements Observer{ private float temperature; private float pressure; private float humidity; /** * @Date 2021/12/28 15:58 * @Param * @param temperature * @param pressure * @param humidity * @Return void * @MetodName update * @Author wang * @Description該方法將更新后的數(shù)據(jù)推送至該觀察者,觀察者打印 */ @Override public void update(float temperature, float pressure, float humidity) { this.temperature = temperature; this.pressure = pressure; this.humidity = humidity; display(); } /** * @Date 2021/12/28 15:59 * @Param * @Return void * @MetodName display * @Author wang * @Description 該方法顯示更新的數(shù)據(jù) */ public void display() { System.out.println("測(cè)試顯示當(dāng)前氣溫:" + temperature + "度"); System.out.println("測(cè)試顯示當(dāng)前壓力:" + pressure + "帕"); System.out.println("測(cè)試顯示當(dāng)前濕度:" + humidity + "Rh"); } }
具體觀察者2:
package com.observerPattern.weatherCase; /** * @author wang * @version 1.0 * @packageName com.observerPattern.weatherCase * @className SinaNet * @date 2021/12/28 16:21 * @Description 新浪網(wǎng)站作為一個(gè)觀察者 */ public class SinaNet implements Observer{ private float temperature; private float pressure; private float humidity; /** * @Date 2021/12/28 15:58 * @Param * @param temperature * @param pressure * @param humidity * @Return void * @MetodName update * @Author wang * @Description該方法將更新后的數(shù)據(jù)推送至該觀察者,觀察者打印 */ @Override public void update(float temperature, float pressure, float humidity) { this.temperature = temperature; this.pressure = pressure; this.humidity = humidity; display(); } /** * @Date 2021/12/28 15:59 * @Param * @Return void * @MetodName display * @Author wang * @Description 該方法顯示更新的數(shù)據(jù) */ public void display() { System.out.println("=======新浪網(wǎng)站======="); System.out.println("新浪顯示當(dāng)前氣溫:" + temperature + "度"); System.out.println("新浪顯示當(dāng)前壓力:" + pressure + "帕"); System.out.println("新浪顯示當(dāng)前濕度:" + humidity + "Rh"); } }
客戶端測(cè)試類
package com.observerPattern.weatherCase; /** * @author wang * @version 1.0 * @packageName com.observerPattern.weatherCase * @className ClientTest * @date 2021/12/28 16:12 * @Description 客戶端測(cè)試代碼,測(cè)試觀察者模式 */ public class ClientTest { public static void main(String[] args) { //創(chuàng)建一個(gè)weatherDate具體目標(biāo) WeatherDate weatherDate = new WeatherDate(); //創(chuàng)建一個(gè)觀察者 CurrentCondition currentCondition = new CurrentCondition(); //注冊(cè)一個(gè)觀察者 weatherDate.registerObserver(currentCondition); //注冊(cè)新浪 SinaNet sinaNet = new SinaNet(); weatherDate.registerObserver(sinaNet); //測(cè)試更新 System.out.println("通知給各觀察者"); weatherDate.setDate(3,65,12); //測(cè)試移除 weatherDate.removeObserver(currentCondition); System.out.println("========================"); System.out.println("第二次更新"); weatherDate.setDate(6,88,16); } } /* 通知給各觀察者 測(cè)試顯示當(dāng)前氣溫:3.0度 測(cè)試顯示當(dāng)前壓力:65.0帕 測(cè)試顯示當(dāng)前濕度:12.0Rh =======新浪網(wǎng)站======= 新浪顯示當(dāng)前氣溫:3.0度 新浪顯示當(dāng)前壓力:65.0帕 新浪顯示當(dāng)前濕度:12.0Rh ======================== 第二次更新 =======新浪網(wǎng)站======= 新浪顯示當(dāng)前氣溫:6.0度 新浪顯示當(dāng)前壓力:88.0帕 新浪顯示當(dāng)前濕度:16.0Rh */
這種好處是我們?nèi)绻行碌木W(wǎng)站的加入,那么直接添加一個(gè)觀察者類即可,不用修改代碼
以及刪除,注冊(cè)都是獨(dú)立開來的。
總結(jié)
本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
Spring中使用自定義ThreadLocal存儲(chǔ)導(dǎo)致的坑及解決
這篇文章主要介紹了Spring中使用自定義ThreadLocal存儲(chǔ)導(dǎo)致的坑及解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-122020年IntelliJ IDEA最新最詳細(xì)配置圖文教程詳解
這篇文章主要介紹了2020年IntelliJ IDEA最新最詳細(xì)配置圖文教程詳解,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-02-02Spring Boot系列教程之7步集成RabbitMQ的方法
RabbitMQ 即一個(gè)消息隊(duì)列,主要是用來實(shí)現(xiàn)應(yīng)用程序的異步和解耦,同時(shí)也能起到消息緩沖,消息分發(fā)的作用。下面這篇文章主要給大家介紹了關(guān)于Spring Boot之7步集成RabbitMQ的相關(guān)資料,需要的朋友可以參考下2018-11-11IDEA 插件 mapper和xml互相跳轉(zhuǎn)操作
這篇文章主要介紹了IDEA 插件 mapper和xml互相跳轉(zhuǎn)操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2021-02-02@ConfigurationProperties加載外部配置方式
這篇文章主要介紹了@ConfigurationProperties加載外部配置方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-03-03Java實(shí)現(xiàn)RedisUtils操作五大集合(增刪改查)
本文主要介紹了Java實(shí)現(xiàn)RedisUtils操作五大集合,文中通過示例代碼介紹的非常詳細(xì),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-07-07基于params、@PathVariabl和@RequestParam的用法與區(qū)別說明
這篇文章主要介紹了方法參數(shù)相關(guān)屬性params、@PathVariabl和@RequestParam用法與區(qū)別,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08