Java觀察者模式的深入了解
一、觀察者模式的定義和特點(diǎn)
觀察者模式的定義:
指多個(gè)對(duì)象間存在一對(duì)多的依賴關(guān)系,當(dāng)一個(gè)對(duì)象的狀態(tài)發(fā)生改變時(shí),所有依賴于它的對(duì)象都得到通知并被自動(dòng)更新。這種模式有時(shí)又稱作發(fā)布-訂閱模式、模型-視圖模式,它是對(duì)象行為型模式。
特點(diǎn):
1.降低了目標(biāo)與觀察者之間的耦合關(guān)系,兩者之間是抽象耦合關(guān)系。符合依賴倒置原則。
2.目標(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ā)送天氣信息,且我們可以自由的注冊或者移除想要發(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 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 注冊觀察者方法
*/
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注冊一個(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("測試顯示當(dāng)前氣溫:" + temperature + "度");
System.out.println("測試顯示當(dāng)前壓力:" + pressure + "帕");
System.out.println("測試顯示當(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");
}
}客戶端測試類
package com.observerPattern.weatherCase;
/**
* @author wang
* @version 1.0
* @packageName com.observerPattern.weatherCase
* @className ClientTest
* @date 2021/12/28 16:12
* @Description 客戶端測試代碼,測試觀察者模式
*/
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();
//注冊一個(gè)觀察者
weatherDate.registerObserver(currentCondition);
//注冊新浪
SinaNet sinaNet = new SinaNet();
weatherDate.registerObserver(sinaNet);
//測試更新
System.out.println("通知給各觀察者");
weatherDate.setDate(3,65,12);
//測試移除
weatherDate.removeObserver(currentCondition);
System.out.println("========================");
System.out.println("第二次更新");
weatherDate.setDate(6,88,16);
}
}
/*
通知給各觀察者
測試顯示當(dāng)前氣溫:3.0度
測試顯示當(dāng)前壓力:65.0帕
測試顯示當(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è)觀察者類即可,不用修改代碼
以及刪除,注冊都是獨(dú)立開來的。
總結(jié)
本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
SpringBoot?Security從入門到實(shí)戰(zhàn)示例教程
Spring?Security是一個(gè)功能強(qiáng)大且高度可定制的身份驗(yàn)證和訪問控制框架,接下來通過本文給大家介紹SpringBoot?Security從入門到實(shí)戰(zhàn)示例教程,感興趣的朋友一起看看吧2022-05-05
編寫Spring MVC控制器的14個(gè)技巧(小結(jié))
這篇文章主要介紹了編寫Spring MVC控制器的14個(gè)技巧,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11
Spring Boot 把配置文件和日志文件放到j(luò)ar外部
如果不想使用默認(rèn)的application.properties,而想將屬性文件放到j(luò)ar包外面,怎么做呢?下面小編給大家?guī)砹藘煞N方法解決Spring Boot 把配置文件和日志文件放到j(luò)ar外部問題,感興趣的朋友一起看看吧2018-02-02
Java 動(dòng)態(tài)模擬操作系統(tǒng)進(jìn)程調(diào)度算法
這篇文章主要介紹了采用java語言編程模擬N個(gè)進(jìn)程采用動(dòng)態(tài)高優(yōu)先權(quán)優(yōu)先進(jìn)程調(diào)度算法。文中代碼具有一定的學(xué)習(xí)價(jià)值,感興趣的小伙伴可以了解一下2021-12-12

