观察者模式(Observer Pattern)详解
观察者模式(Observer Pattern)是一种行为型设计模式,用于定义对象间的一种 一对多 的依赖关系,使得当一个对象的状态发生改变时,其相关依赖对象能够自动接收到通知并进行相应的更新。观察者模式广泛应用于事件驱动的系统中,是 GUI 应用程序、事件系统、消息推送等场景中的核心设计模式。
1. 观察者模式的定义
1.1 什么是观察者模式?
观察者模式,也称为 发布-订阅模式(Publish-Subscribe Pattern),其核心思想是 “观察者订阅被观察者”。当被观察者(也称为 主题(Subject))的状态发生变化时,所有已订阅的观察者(Observer)都会收到通知,并根据通知做出相应的反应。
1.2 观察者模式的结构
观察者模式通常包含以下几个角色:
- Subject(被观察者/主题):
- 维护一个观察者列表。
- 提供注册、注销观察者的方法。
- 当状态改变时,通知所有观察者。
- Observer(观察者):
- 定义一个更新接口,用于接收通知。
- ConcreteSubject(具体被观察者):
- 继承
Subject
,实现具体的通知逻辑。
- 继承
- ConcreteObserver(具体观察者):
- 继承
Observer
,实现具体的响应逻辑。
- 继承
2. 观察者模式的类图
+-----------------+ +----------------+
| Subject |<>----->| Observer |
+-----------------+ +----------------+
| + attach() | | + update() |
| + detach() | +----------------+
| + notify() | ^
+-----------------+ |
^ |
| |
+-----------------+ +--------------------+
| ConcreteSubject | | ConcreteObserver |
+-----------------+ +--------------------+
| - state | | - observerState |
| + getState() | | + update() |
| + setState() | +--------------------+
+-----------------+
3. 观察者模式的实现
以下是一个简单的观察者模式示例。在该示例中,假设有一个天气站(WeatherStation)作为被观察者,多个显示设备(DisplayDevice)作为观察者,天气站会将温度变化通知给所有已注册的显示设备。
3.1 Java 示例代码
import java.util.ArrayList;
import java.util.List;
// 观察者接口
interface Observer {
void update(float temperature);
}
// 被观察者接口
interface Subject {
void registerObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers();
}
// 具体的被观察者:天气站
class WeatherStation implements Subject {
private List<Observer> observers;
private float temperature;
public WeatherStation() {
this.observers = new ArrayList<>();
}
@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);
}
}
// 设置新的温度并通知观察者
public void setTemperature(float temperature) {
this.temperature = temperature;
notifyObservers();
}
}
// 具体的观察者:手机显示设备
class PhoneDisplay implements Observer {
private String name;
public PhoneDisplay(String name) {
this.name = name;
}
@Override
public void update(float temperature) {
System.out.println(name + " 显示温度更新为: " + temperature + "°C");
}
}
// 具体的观察者:平板显示设备
class TabletDisplay implements Observer {
@Override
public void update(float temperature) {
System.out.println("平板设备显示温度更新为: " + temperature + "°C");
}
}
// 测试客户端
public class ObserverPatternDemo {
public static void main(String[] args) {
// 创建被观察者(天气站)
WeatherStation weatherStation = new WeatherStation();
// 创建观察者
PhoneDisplay phoneDisplay = new PhoneDisplay("手机设备1");
TabletDisplay tabletDisplay = new TabletDisplay();
// 注册观察者
weatherStation.registerObserver(phoneDisplay);
weatherStation.registerObserver(tabletDisplay);
// 更新天气并通知观察者
weatherStation.setTemperature(25.5f);
weatherStation.setTemperature(30.0f);
// 移除一个观察者
weatherStation.removeObserver(phoneDisplay);
weatherStation.setTemperature(28.5f);
}
}
输出结果:
手机设备1 显示温度更新为: 25.5°C
平板设备显示温度更新为: 25.5°C
手机设备1 显示温度更新为: 30.0°C
平板设备显示温度更新为: 30.0°C
平板设备显示温度更新为: 28.5°C
4. 观察者模式的应用场景
观察者模式广泛应用于各种需要事件通知的场景中:
- GUI 事件监听:
- 在图形用户界面中,如按钮点击、文本框输入变化等事件处理机制。
- MVC 架构:
- 在 MVC 设计模式中,视图(View)会观察模型(Model)的变化,当模型的数据发生变化时,视图会自动更新。
- 推送通知系统:
- 应用程序中的消息推送服务、实时更新、订阅发布系统(如 RSS、社交媒体通知)。
- 缓存刷新机制:
- 当缓存数据源发生变化时,通知所有相关缓存进行刷新。
- 数据库触发器:
- 在数据库中,当某个表的数据发生变化时,可以触发通知其他表或系统。
5. 观察者模式的优缺点
5.1 优点
- 降低耦合度:被观察者和观察者之间是松耦合关系。被观察者无需知道观察者的具体实现,符合“依赖倒置原则”。
- 支持广播通信:一个被观察者可以同时通知多个观察者,扩展性强。
- 符合开闭原则:可以在不修改被观察者的情况下添加新的观察者,实现系统的灵活扩展。
5.2 缺点
- 潜在的性能问题:如果有大量的观察者,通知所有观察者可能会带来一定的性能开销。
- 通知顺序不确定:观察者的通知顺序是不确定的,可能会导致数据不一致的情况。
- 内存泄漏:如果观察者没有正确注销,可能会导致内存泄漏,尤其是在 Java 等需要手动管理观察者注册和注销的语言中。
6. 观察者模式的扩展
6.1 推模型 vs 拉模型
- 推模型(Push Model):
- 被观察者主动将变更数据推送给观察者。适用于数据量小、变化频繁的场景。
- 拉模型(Pull Model):
- 被观察者仅通知观察者状态已发生变化,观察者自行拉取所需的数据。适用于数据量大、变化不频繁的场景。
6.2 Java 内置的观察者模式
Java 标准库提供了 java.util.Observable
类和 java.util.Observer
接口来实现观察者模式。
示例:
import java.util.Observable;
import java.util.Observer;
class NewsAgency extends Observable {
private String news;
public void setNews(String news) {
this.news = news;
setChanged(); // 标记状态已改变
notifyObservers(news);
}
}
class NewsChannel implements Observer {
@Override
public void update(Observable o, Object arg) {
System.out.println("新闻频道收到新闻更新: " + arg);
}
}
public class JavaObserverExample {
public static void main(String[] args) {
NewsAgency newsAgency = new NewsAgency();
NewsChannel newsChannel = new NewsChannel();
newsAgency.addObserver(newsChannel);
newsAgency.setNews("新冠疫情最新动态!");
}
}
输出:
新闻频道收到新闻更新: 新冠疫情最新动态!
6.3 RxJava 的响应式编程
- 在响应式编程中,观察者模式的思想被广泛应用。RxJava 通过
Observable
和Observer
提供了更加灵活和功能丰富的 API 来处理异步数据流。
7. 总结
观察者模式是一种非常常用的设计模式,在许多场景下都能提升系统的扩展性和灵活性。它不仅降低了对象之间的耦合度,还能实现事件驱动的机制,使得系统能够动态响应外部的变化。
- 优点:松耦合、易于扩展、支持广播通信。
- 缺点:可能导致性能开销、内存泄漏风险。
- 适用场景:事件监听、推送通知、MVC 设计模式、数据缓存机制。