👋hi,我不是一名外包公司的员工,也不会偷吃茶水间的零食,我的梦想是能写高端CRUD
🔥 2025本人正在沉淀中... 博客更新速度++
📫 欢迎+V: flzjcsg2,我们共同讨论Java深渊的奥秘
🎵 当你的天空突然下了大雨,那是我在为你炸乌云
文章目录
- 一、入门
- 二、观察者模式在源码中运用
- [Java 中的 java.util.Observer 和 java.util.Observable](#Java 中的 java.util.Observer 和 java.util.Observable)
- [Spring 框架中的事件机制](#Spring 框架中的事件机制)
- 三、总结
- 参考
一、入门
什么是观察者模式?
观察者模式(Observer Pattern)是一种行为设计模式,它定义了对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会收到通知并自动更新。
为什么要观察者模式?
假设我们正在开发一个天气预报系统,其中:
- WeatherStation(气象站):负责收集天气数据(如温度、湿度等)。
- Display(显示设备):负责显示天气数据,比如手机App、电子屏等。
当气象站的数据更新时,所有显示设备都需要实时更新显示内容。
下面是没有观察者模式时的实现:
java
class WeatherStation {
private float temperature;
private float humidity;
private PhoneDisplay phoneDisplay;
private TVDisplay tvDisplay;
public void setPhoneDisplay(PhoneDisplay phoneDisplay) {
this.phoneDisplay = phoneDisplay;
}
public void setTVDisplay(TVDisplay tvDisplay) {
this.tvDisplay = tvDisplay;
}
public void removePhoneDisplay() {
phoneDisplay = null;
}
public void removeTVDisplay() {
phoneDisplay = null;
}
public void setMeasurements(float temperature, float humidity) {
this.temperature = temperature;
this.humidity = humidity;
// 手动调用显示设备的更新方法
if (phoneDisplay != null) {
phoneDisplay.update(temperature, humidity);
}
if (tvDisplay != null) {
tvDisplay.update(temperature, humidity);
}
}
}
紧耦合
如果没有观察者模式,气象站需要直接知道所有显示设备的存在,并手动调用它们的更新方法。例如:
问题:
- 气象站和显示设备之间是紧耦合的,气象站需要知道所有显示设备的具体实现。
- 如果新增一个显示设备(比如智能手表),需要修改气象站的代码,违反了开闭原则(对扩展开放,对修改关闭)。
难以动态管理依赖
如果显示设备需要动态添加或移除(比如用户关闭了某个显示设备),气象站需要手动管理这些设备的引用。
扩展性差
如果未来需要支持更多类型的观察者(比如日志记录器、报警系统等),气象站的代码会变得越来越臃肿,难以维护。
怎么实现观察者模式?
在观察者模式中有如下角色:
- Subject:抽象主题(抽象被观察者),抽象主题角色把所有观察者对象保存在一个集合里,每个主题都可以有任意数量的观察者,抽象主题提供一个接口,可以增加和删除观察者对象。
- ConcreteSubject:具体主题(具体被观察者),该角色将有关状态存入具体观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发送通知。
- Observer:抽象观察者,是观察者的抽象类,它定义了一个更新接口,使得在得到主题更改通知时更新自己。
- ConcrereObserver:具体观察者,实现抽象观察者定义的更新接口,以便在得到主题更改通知时更新自身的状态。
【案例】天气站 - 改
Observer观察者 : Observer
接口
java
interface Observer {
void update(float temperature, float humidity);
}
Subject主题 : subject
接口
java
interface Subject {
void registerObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers();
}
实现具体主题(气象站) : WeatherStation
类
java
class WeatherStation implements Subject {
private List<Observer> observers = new ArrayList<>();
private float temperature;
private float humidity;
@Override
public void registerObserver(Observer 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);
}
}
public void setMeasurements(float temperature, float humidity) {
this.temperature = temperature;
this.humidity = humidity;
notifyObservers(); // 通知所有观察者
}
}
实现具体观察者(显示设备) : PhoneDisplay
类和TVDisplay
类
java
class PhoneDisplay implements Observer {
@Override
public void update(float temperature, float humidity) {
System.out.println("手机显示:温度 = " + temperature + ",湿度 = " + humidity);
}
}
class TVDisplay implements Observer {
@Override
public void update(float temperature, float humidity) {
System.out.println("电视显示:温度 = " + temperature + ",湿度 = " + humidity);
}
}
测试类
java
public class WeatherApp {
public static void main(String[] args) {
WeatherStation weatherStation = new WeatherStation();
Observer phoneDisplay = new PhoneDisplay();
Observer tvDisplay = new TVDisplay();
weatherStation.registerObserver(phoneDisplay);
weatherStation.registerObserver(tvDisplay);
// 更新天气数据
weatherStation.setMeasurements(25.5f, 60.0f);
// 移除一个观察者
weatherStation.removeObserver(tvDisplay);
// 再次更新天气数据
weatherStation.setMeasurements(26.0f, 58.0f);
}
}
运行结果
shell
手机显示:温度 = 25.5,湿度 = 60.0
电视显示:温度 = 25.5,湿度 = 60.0
手机显示:温度 = 26.0,湿度 = 58.0
二、观察者模式在源码中运用
Java 中的 java.util.Observer 和 java.util.Observable
Java 标准库中提供了观察者模式的实现,分别是 Observer 接口和 Observable 类。
- Observable 是被观察者的基类,内部维护了一个观察者列表,并提供了 addObserver、deleteObserver 和 notifyObservers 方法。
- Observer 是观察者接口,定义了 update 方法,用于接收通知。
Observer和Observable的使用
被观察者(具体主题) :WeatherData
类
java
// 被观察者(主题)
class WeatherData extends Observable {
private float temperature;
private float humidity;
public void setMeasurements(float temperature, float humidity) {
this.temperature = temperature;
this.humidity = humidity;
setChanged(); // 标记状态已改变
notifyObservers(); // 通知观察者
}
public float getTemperature() {
return temperature;
}
public float getHumidity() {
return humidity;
}
}
观察者(具体观察者) : Display
类
java
// 观察者
class Display implements Observer {
@Override
public void update(Observable o, Object arg) {
if (o instanceof WeatherData) {
WeatherData weatherData = (WeatherData) o;
float temperature = weatherData.getTemperature();
float humidity = weatherData.getHumidity();
System.out.println("当前温度: " + temperature + ",湿度: " + humidity);
}
}
}
测试
java
public class ObserverPatternDemo {
public static void main(String[] args) {
WeatherData weatherData = new WeatherData();
Display display = new Display();
weatherData.addObserver(display); // 注册观察者
weatherData.setMeasurements(25.5f, 60.0f); // 更新数据并通知观察者
}
}
输出结果
shell
当前温度: 25.5,湿度: 60.0
Observer和Observable的源码实现
观察者 :Observer
类,入参Observable o
:被观察的对象(主题)和 Object arg
:传递给观察者的额外参数(可选)。
java
public interface Observer {
void update(Observable o, Object arg);
}
主题 :Observable
类。
我们可以看到Vector<Observer>
存储观察者列表。并且因为加了synchronized
关键字,这些方法都是线程安全的。notifyObservers
方法会遍历观察者列表,并调用每个观察者的update
方法。
java
public class Observable {
// 标记对象是否已改变
private boolean changed = false;
// 观察者列表(使用 Vector 保证线程安全)
private Vector<Observer> obs;
public Observable() {
obs = new Vector<>();
}
// 添加观察者
public synchronized void addObserver(Observer o) {
if (o == null)
throw new NullPointerException();
if (!obs.contains(o)) {
obs.addElement(o);
}
}
// 删除观察者
public synchronized void deleteObserver(Observer o) {
obs.removeElement(o);
}
// 通知所有观察者(无参数)
public void notifyObservers() {
notifyObservers(null);
}
// 通知所有观察者(带参数)
public void notifyObservers(Object arg) {
Observer[] arrLocal;
// 同步块,确保线程安全
synchronized (this) {
if (!changed) // 如果没有变化,直接返回
return;
arrLocal = obs.toArray(new Observer[obs.size()]);
clearChanged(); // 重置变化标志
}
// 遍历观察者列表,调用 update 方法
for (Observer observer : arrLocal) {
observer.update(this, arg);
}
}
// 删除所有观察者
public synchronized void deleteObservers() {
obs.removeAllElements();
}
// 标记对象已改变
protected synchronized void setChanged() {
changed = true;
}
// 重置变化标志
protected synchronized void clearChanged() {
changed = false;
}
// 检查对象是否已改变
public synchronized boolean hasChanged() {
return changed;
}
// 返回观察者数量
public synchronized int countObservers() {
return obs.size();
}
}
Spring 框架中的事件机制
Spring 框架中的事件机制是基于观察者模式实现的。它允许开发者定义自定义事件,并通过监听器(观察者)来处理这些事件。
Spring事件机制的使用
自定义事件
java
class CustomEvent extends ApplicationEvent {
private String message;
public CustomEvent(Object source, String message) {
super(source);
this.message = message;
}
public String getMessage() {
return message;
}
}
事件监听器(观察者)
java
@Component
class CustomEventListener implements ApplicationListener<CustomEvent> {
@Override
public void onApplicationEvent(CustomEvent event) {
System.out.println("收到事件: " + event.getMessage());
}
}
事件发布者
java
@Component
class CustomEventPublisher {
private final AnnotationConfigApplicationContext context;
public CustomEventPublisher(AnnotationConfigApplicationContext context) {
this.context = context;
}
public void publishEvent(String message) {
context.publishEvent(new CustomEvent(this, message));
}
}
配置类
java
@Configuration
@ComponentScan
class AppConfig {}
测试类
java
public class SpringEventDemo {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
CustomEventPublisher publisher = context.getBean(CustomEventPublisher.class);
publisher.publishEvent("Hello, Spring Event!");
context.close();
}
}
输出内容
shell
收到事件: Hello, Spring Event!
Spring的事件机质的源码实现
Spring 事件机制的核心组件包括:
ApplicationEvent
:事件的基类,所有自定义事件都需要继承它。对应观察者模式中的"事件"。ApplicationListener
:观察者接口,定义了处理事件的方法。对应观察者模式中的"观察者"。ApplicationEventPublisher
:事件发布者接口,用于发布事件。对应观察者模式中的"主题"。ApplicationEventMulticaster
:事件广播器,负责将事件分发给所有监听器。类似于观察者模式中的"通知机制"。
ApplicationEvent
类 :ApplicationEvent
是所有事件的基类,它继承自 java.util.EventObject
。
java
public abstract class ApplicationEvent extends EventObject {
private final long timestamp; // timestamp: 事件发生的时间戳。
public ApplicationEvent(Object source) { // source:事件源,通常是发布事件的对象。
super(source);
this.timestamp = System.currentTimeMillis();
}
public final long getTimestamp() {
return this.timestamp;
}
}
ApplicationListener
接口 : ApplicationListener
是观察者接口,定义了处理事件的方法。
java
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
void onApplicationEvent(E event); // 当事件发生时,会调用此方法。
}
ApplicationEventPublisher
接口: 是事件发布者接口,用于发布事件。
java
@FunctionalInterface
public interface ApplicationEventPublisher {
default void publishEvent(ApplicationEvent event) {
publishEvent((Object) event);
}
void publishEvent(Object event);
}
ApplicationEventMulticaster
接口:是事件广播器接口,负责将事件分发给所有监听器。
java
public interface ApplicationEventMulticaster {
void addApplicationListener(ApplicationListener<?> listener);
void addApplicationListenerBean(String listenerBeanName);
void removeApplicationListener(ApplicationListener<?> listener);
void removeApplicationListenerBean(String listenerBeanName);
void removeAllListeners();
void multicastEvent(ApplicationEvent event);
void multicastEvent(ApplicationEvent event, ResolvableType eventType);
}
SimpleApplicationEventMulticaster
类 :SimpleApplicationEventMulticaster
是ApplicationEventMulticaster
的默认实现类。
java
public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
// 遍历所有监听器,并调用 onApplicationEvent 方法。
@Override
public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
Executor executor = getTaskExecutor();
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
} else {
invokeListener(listener, event);
}
}
}
// 实际调用监听器的 onApplicationEvent 方法。
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
try {
listener.onApplicationEvent(event);
} catch (ClassCastException ex) {
// 处理类型转换异常
}
}
}
三、总结
观察者模式的优点
解耦 :主题(Subject)和观察者(Observer)之间是松耦合的(主题不需要知道观察者的具体实现,只需要知道观察者接口,观察者也不需要知道主题的具体实现,只需要实现观察者接口)
动态管理依赖:观察者可以动态注册和注销,而不需要修改主题的代码。支持运行时动态添加或移除观察者,灵活性高。
符合开闭原则 :可以轻松添加新的观察者,而不需要修改主题的代码。主题的代码不需要因为观察者的变化而修改。
广播通信 :主题可以一次性通知所有观察者,适合一对多的通信场景。观察者可以根据需要选择是否响应通知。
职责分离:主题负责维护状态和通知观察者。观察者负责处理状态变化的逻辑。职责分离使得代码更加清晰和可维护。
观察者模式的缺点
- 性能问题
- 如果观察者数量非常多,通知所有观察者可能会消耗大量时间。
- 如果观察者的处理逻辑复杂,可能会导致性能瓶颈。
- 内存泄漏
- 如果观察者没有正确注销,可能会导致观察者无法被垃圾回收,从而引发内存泄漏。
- 特别是在长时间运行的应用中,需要特别注意观察者的生命周期管理。
- 调试困难
- 由于观察者和主题是松耦合的,调试时可能难以追踪事件的传递路径。
- 如果观察者的处理逻辑出现问题,可能不容易定位问题根源。
- 事件顺序不确定
- 观察者收到通知的顺序通常是不确定的,如果业务逻辑对顺序有要求,可能需要额外的处理。
观察者模式的适用场景
- 事件驱动系统
- 当一个对象的状态变化需要触发其他对象的操作时,可以使用观察者模式。
- 例如:GUI 框架中的按钮点击事件、Spring 框架中的事件机制。
- 一对多的依赖关系
- 当一个对象的状态变化需要通知多个其他对象时,可以使用观察者模式。
- 例如:气象站和多个显示设备的关系。
- 跨系统的消息通知
- 在分布式系统中,观察者模式可以用于实现消息的发布和订阅。
- 例如:消息队列(MQ)中的发布-订阅模型。
- 状态变化的广播
- 当一个对象的状态变化需要广播给多个对象时,可以使用观察者模式。
- 例如:游戏中的角色状态变化通知其他系统(如 UI、音效等)。
- 解耦业务逻辑
- 当需要将业务逻辑解耦为多个独立的模块时,可以使用观察者模式。
- 例如:订单系统中的订单状态变化通知库存系统、物流系统等。