本文目录导读:

我将通过一个天气预报系统的案例来展示观察者模式的实现。
案例场景
气象站需要将天气数据(温度、湿度、气压)的变化通知到多个显示设备(如手机App、网页、电视等),当气象数据更新时,所有注册的显示设备都能自动收到通知。
类结构
┌─────────────────┐ ┌──────────────────────┐
│ Subject │ │ Observer │
│ (接口/抽象类) │ │ (接口) │
├─────────────────┤ ├──────────────────────┤
│ +register() │ │ +update() │
│ +remove() │ └──────────────────────┘
│ +notify() │ △
└─────────────────┘ │
△ │
│ │
┌────────┴────────┐ ┌──────────┴──────────┐
│ WeatherData │ │ DisplayElement │
│ (具体主题) │ │ (具体观察者) │
└─────────────────┘ └─────────────────────┘
完整代码实现
观察者接口
// 观察者接口
public interface Observer {
void update(float temperature, float humidity, float pressure);
}
主题接口
// 主题接口
public interface Subject {
void registerObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers();
}
显示元素接口
// 显示元素接口
public interface DisplayElement {
void display();
}
具体主题 - 天气预报数据
import java.util.ArrayList;
import java.util.List;
// 具体主题:天气预报数据
public class WeatherData implements Subject {
private List<Observer> observers;
private float temperature;
private float humidity;
private float pressure;
public WeatherData() {
observers = new ArrayList<>();
}
@Override
public void registerObserver(Observer observer) {
if (observer != null && !observers.contains(observer)) {
observers.add(observer);
}
}
@Override
public void removeObserver(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.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();
}
// 获取温度
public float getTemperature() {
return temperature;
}
}
具体观察者 - 手机App显示
// 手机App显示
public class PhoneDisplay implements Observer, DisplayElement {
private float temperature;
private float humidity;
private float pressure;
private Subject weatherData;
public PhoneDisplay(Subject weatherData) {
this.weatherData = weatherData;
weatherData.registerObserver(this);
}
@Override
public void update(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
display();
}
@Override
public void display() {
System.out.println("📱 [手机App显示] 温度: " + temperature + "°C, "
+ "湿度: " + humidity + "%, 气压: " + pressure + "hPa");
}
}
具体观察者 - 网页显示
// 网页显示
public class WebDisplay implements Observer, DisplayElement {
private float temperature;
private float humidity;
private float pressure;
private Subject weatherData;
public WebDisplay(Subject weatherData) {
this.weatherData = weatherData;
weatherData.registerObserver(this);
}
@Override
public void update(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
display();
}
@Override
public void display() {
System.out.println("🌐 [网页显示] 当前天气:"
+ "温度 " + temperature + "°C | "
+ "湿度 " + humidity + "% | "
+ "气压 " + pressure + " hPa");
}
}
具体观察者 - 统计显示(使用拉模型)
// 统计显示:使用拉模型获取数据
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);
}
@Override
public void update(float temperature, float humidity, float pressure) {
tempSum += temperature;
numReadings++;
if (temperature > maxTemp) {
maxTemp = temperature;
}
if (temperature < minTemp) {
minTemp = temperature;
}
display();
}
@Override
public void display() {
System.out.println("📊 [统计显示] 平均温度: " + (tempSum / numReadings)
+ "°C, 最高温度: " + maxTemp + "°C, 最低温度: " + minTemp + "°C");
}
}
测试类
public class WeatherStationTest {
public static void main(String[] args) {
// 创建具体主题
WeatherData weatherData = new WeatherData();
// 创建观察者并注册
PhoneDisplay phoneDisplay = new PhoneDisplay(weatherData);
WebDisplay webDisplay = new WebDisplay(weatherData);
StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData);
System.out.println("===== 天气观测站启动 =====");
System.out.println();
// 模拟天气数据变化
System.out.println("--- 第一次天气更新 ---");
weatherData.setMeasurements(25.5f, 65.0f, 1013.0f);
System.out.println("\n--- 第二次天气更新 ---");
weatherData.setMeasurements(26.2f, 65.4f, 1012.5f);
System.out.println("\n--- 移除手机App观察者 ---");
weatherData.removeObserver(phoneDisplay);
System.out.println("\n--- 第三次天气更新(手机App不再接收) ---");
weatherData.setMeasurements(27.0f, 66.0f, 1012.0f);
// 使用拉模型
System.out.println("\n--- 从主题拉取最新温度 ---");
float currentTemp = weatherData.getTemperature();
System.out.println("当前温度为: " + currentTemp + "°C");
}
}
运行结果
===== 天气观测站启动 =====
--- 第一次天气更新 ---
📱 [手机App显示] 温度: 25.5°C, 湿度: 65.0%, 气压: 1013.0hPa
🌐 [网页显示] 当前天气:温度 25.5°C | 湿度 65.0% | 气压 1013.0 hPa
📊 [统计显示] 平均温度: 25.5°C, 最高温度: 25.5°C, 最低温度: 25.5°C
--- 第二次天气更新 ---
📱 [手机App显示] 温度: 26.2°C, 湿度: 65.4%, 气压: 1012.5hPa
🌐 [网页显示] 当前天气:温度 26.2°C | 湿度 65.4% | 气压 1012.5 hPa
📊 [统计显示] 平均温度: 25.85°C, 最高温度: 26.2°C, 最低温度: 25.5°C
--- 移除手机App观察者 ---
--- 第三次天气更新(手机App不再接收) ---
🌐 [网页显示] 当前天气:温度 27.0°C | 湿度 66.0% | 气压 1012.0 hPa
📊 [统计显示] 平均温度: 26.23°C, 最高温度: 27.0°C, 最低温度: 25.5°C
--- 从主题拉取最新温度 ---
当前温度为: 27.0°C
设计要点
推模型 vs 拉模型
- 推模型:主题将数据推送给观察者(
update(float temp, float humidity, float pressure)) - 拉模型:观察者主动从主题拉取数据(
weatherData.getTemperature())
解耦优势
- 主题(WeatherData)不需要知道观察者的具体类型
- 观察者可以随时注册或取消注册
- 新增观察者类型无需修改主题代码
Java内置支持
Java提供了java.util.Observable和java.util.Observer,但在实际项目中推荐自定义以实现更好的解耦和灵活性。
观察者模式特别适合一对多的对象关系,当主题状态变化时,所有观察者自动收到通知,实现了对象间的松耦合设计。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播