观察者模式(Observer Pattern)
一、模式概述
观察者模式是一种行为型设计模式,它定义了对象之间的一对多依赖关系,当一个对象(主题/被观察者)状态改变时,所有依赖于它的对象(观察者)都会得到通知并自动更新。
核心思想:将观察者和被观察者解耦,观察者不需要知道被观察者的具体实现,只需要订阅即可。当被观察者状态变化时,自动通知所有观察者。
二、角色说明
| 角色 | 说明 |
|---|---|
| Subject(主题/被观察者) | 持有观察者列表,状态变化时通知所有观察者 |
| Observer(观察者) | 定义更新接口,接收主题的通知 |
| ConcreteSubject(具体主题) | 存储状态,状态变化时通知所有观察者 |
| ConcreteObserver(具体观察者) | 实现更新接口,根据通知更新自身状态 |
三、生活中的例子
- 微信公众号:你关注了一个公众号(订阅),有新文章时自动推送给你(通知)
- 天气预报:气象站(被观察者)数据变化时,多个显示屏(观察者)自动更新
- 报纸订阅:报社(被观察者)出版新报纸时,订阅者(观察者)自动收到
- 股票行情:股价变化时,所有关注该股票的用户自动收到通知
- DOM 事件:按钮点击时,所有注册的事件监听器自动被调用
四、示例1:股票价格观察者
问题场景
多个投资者关注同一只股票的价格变化,价格更新时自动通知所有投资者。
java
import java.util.ArrayList;
import java.util.List;
import java.util.HashMap;
import java.util.Map;
// ========== 观察者接口 ==========
/**
* 股票观察者接口
*/
interface StockObserver {
void update(String stockName, double price, double change);
String getObserverName();
}
// ========== 主题接口 ==========
/**
* 股票主题接口
*/
interface StockSubject {
void addObserver(StockObserver observer);
void removeObserver(StockObserver observer);
void notifyObservers();
}
// ========== 具体主题 ==========
/**
* 股票(具体主题)
*/
class Stock implements StockSubject {
private final String name;
private double price;
private double previousPrice;
private final List<StockObserver> observers = new ArrayList<>();
public Stock(String name, double initialPrice) {
this.name = name;
this.price = initialPrice;
this.previousPrice = initialPrice;
}
@Override
public void addObserver(StockObserver observer) {
observers.add(observer);
System.out.println(" [股票 " + name + "] " + observer.getObserverName() + " 已订阅");
}
@Override
public void removeObserver(StockObserver observer) {
observers.remove(observer);
System.out.println(" [股票 " + name + "] " + observer.getObserverName() + " 已取消订阅");
}
@Override
public void notifyObservers() {
double change = price - previousPrice;
double changePercent = (change / previousPrice) * 100;
for (StockObserver observer : observers) {
observer.update(name, price, changePercent);
}
}
/**
* 股价变化时自动通知观察者
*/
public void setPrice(double newPrice) {
this.previousPrice = this.price;
this.price = newPrice;
System.out.println("\n[股票 " + name + "] 价格变化: "
+ previousPrice + " -> " + newPrice);
notifyObservers();
}
public String getName() { return name; }
public double getPrice() { return price; }
}
// ========== 具体观察者 ==========
/**
* 个人投资者
*/
class IndividualInvestor implements StockObserver {
private String name;
private double budget;
private Map<String, Double> holdings = new HashMap<>();
public IndividualInvestor(String name, double budget) {
this.name = name;
this.budget = budget;
}
@Override
public String getObserverName() { return name; }
@Override
public void update(String stockName, double price, double change) {
System.out.println(" [投资者 " + name + "] 收到通知: "
+ stockName + " 当前价格: " + price
+ " 变化: " + String.format("%+.2f%%", change));
// 简单的投资策略:涨了卖,跌了买
if (change < -2.0 && budget >= price) {
System.out.println(" -> " + name + " 决定买入 " + stockName);
buy(stockName, price);
} else if (change > 3.0 && holdings.containsKey(stockName)) {
System.out.println(" -> " + name + " 决定卖出 " + stockName);
sell(stockName, price);
}
}
private void buy(String stockName, double price) {
budget -= price;
holdings.merge(stockName, price, Double::sum);
System.out.println(" -> 买入成功,剩余预算: " + budget);
}
private void sell(String stockName, double price) {
budget += price;
holdings.remove(stockName);
System.out.println(" -> 卖出成功,当前预算: " + budget);
}
}
/**
* 机构投资者(基金公司)
*/
class InstitutionalInvestor implements StockObserver {
private String name;
private double totalInvestment;
public InstitutionalInvestor(String name) {
this.name = name;
}
@Override
public String getObserverName() { return name; }
@Override
public void update(String stockName, double price, double change) {
System.out.println(" [机构 " + name + "] 收到通知: "
+ stockName + " 价格: " + price
+ " 变化: " + String.format("%+.2f%%", change));
// 机构的投资策略更复杂
if (Math.abs(change) > 5.0) {
System.out.println(" -> [风控] " + name + " 触发大波动警报!变化: "
+ String.format("%+.2f%%", change));
}
}
}
/**
* 股价监控面板
*/
class StockDisplay implements StockObserver {
private String name;
public StockDisplay(String name) {
this.name = name;
}
@Override
public String getObserverName() { return name; }
@Override
public void update(String stockName, double price, double change) {
String arrow = change > 0 ? "↑" : change < 0 ? "↓" : "→";
System.out.println(" [面板 " + name + "] " + stockName
+ ": " + price + " " + arrow
+ " " + String.format("%+.2f%%", change));
}
}
// ========== 客户端代码 ==========
public class StockObserverDemo {
public static void main(String[] args) {
// 创建股票(主题)
Stock appleStock = new Stock("AAPL", 150.0);
Stock googleStock = new Stock("GOOGL", 2800.0);
// 创建观察者
StockObserver alice = new IndividualInvestor("Alice", 10000);
StockObserver bob = new IndividualInvestor("Bob", 5000);
StockObserver fund = new InstitutionalInvestor("高盛基金");
StockObserver display1 = new StockDisplay("大厅显示屏");
StockObserver display2 = new StockDisplay("手机App");
// 订阅
System.out.println("===== 订阅股票 =====");
appleStock.addObserver(alice);
appleStock.addObserver(bob);
appleStock.addObserver(fund);
appleStock.addObserver(display1);
googleStock.addObserver(display2);
googleStock.addObserver(alice);
// 股价变化
System.out.println("\n===== AAPL 价格变化 =====");
appleStock.setPrice(155.0);
System.out.println("\n===== GOOGL 大幅下跌 =====");
googleStock.setPrice(2650.0);
// 取消订阅
System.out.println("\n===== 取消订阅 =====");
appleStock.removeObserver(bob);
System.out.println("\n===== AAPL 再次变化 =====");
appleStock.setPrice(148.0);
}
}五、示例2:自定义事件总线(EventBus)
问题场景
实现一个简单的事件总线,支持发布/订阅模式。
java
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
// ========== 事件基类 ==========
/**
* 事件基类
*/
abstract class Event {
private final long timestamp;
private final String source;
protected Event(String source) {
this.timestamp = System.currentTimeMillis();
this.source = source;
}
public long getTimestamp() { return timestamp; }
public String getSource() { return source; }
}
// ========== 具体事件 ==========
/**
* 用户注册事件
*/
class UserRegisteredEvent extends Event {
private final String username;
public UserRegisteredEvent(String source, String username) {
super(source);
this.username = username;
}
public String getUsername() { return username; }
}
/**
* 订单创建事件
*/
class OrderCreatedEvent extends Event {
private final String orderId;
private final double amount;
public OrderCreatedEvent(String source, String orderId, double amount) {
super(source);
this.orderId = orderId;
this.amount = amount;
}
public String getOrderId() { return orderId; }
public double getAmount() { return amount; }
}
/**
* 支付完成事件
*/
class PaymentCompletedEvent extends Event {
private final String orderId;
private final double amount;
private final String paymentMethod;
public PaymentCompletedEvent(String source, String orderId,
double amount, String paymentMethod) {
super(source);
this.orderId = orderId;
this.amount = amount;
this.paymentMethod = paymentMethod;
}
public String getOrderId() { return orderId; }
public double getAmount() { return amount; }
public String getPaymentMethod() { return paymentMethod; }
}
// ========== 事件监听器接口 ==========
/**
* 事件监听器接口
*/
interface EventListener {
void onEvent(Event event);
}
/**
* 强类型事件监听器
*/
interface TypedEventListener<T extends Event> {
void onEvent(T event);
}
// ========== 事件总线 ==========
/**
* 简单的事件总线实现
* 支持按事件类型订阅和发布
*/
class EventBus {
// 事件类型 -> 监听器列表
private final Map<Class<? extends Event>, List<EventListener>> listeners
= new ConcurrentHashMap<>();
// 强类型监听器
private final Map<Class<? extends Event>, List<TypedEventListener<? extends Event>>> typedListeners
= new ConcurrentHashMap<>();
/**
* 注册事件监听器
*/
public <T extends Event> void subscribe(Class<T> eventType, TypedEventListener<T> listener) {
typedListeners.computeIfAbsent(eventType, k -> new CopyOnWriteArrayList<>())
.add(listener);
System.out.println("[EventBus] 注册监听器: " + eventType.getSimpleName());
}
/**
* 注册通用事件监听器
*/
public void subscribeAll(EventListener listener) {
// 这个监听器会接收所有事件
// 在实际实现中需要特殊处理
System.out.println("[EventBus] 注册全局监听器");
}
/**
* 取消订阅
*/
public <T extends Event> void unsubscribe(Class<T> eventType, TypedEventListener<T> listener) {
List<TypedEventListener<? extends Event>> list = typedListeners.get(eventType);
if (list != null) {
list.remove(listener);
System.out.println("[EventBus] 取消监听: " + eventType.getSimpleName());
}
}
/**
* 发布事件
*/
@SuppressWarnings("unchecked")
public <T extends Event> void publish(T event) {
Class<?> eventType = event.getClass();
System.out.println("[EventBus] 发布事件: " + eventType.getSimpleName()
+ " (来源: " + event.getSource() + ")");
// 通知所有匹配类型的监听器
for (Map.Entry<Class<? extends Event>, List<TypedEventListener<? extends Event>>> entry
: typedListeners.entrySet()) {
if (entry.getKey().isAssignableFrom(eventType)) {
for (TypedEventListener listener : entry.getValue()) {
try {
listener.onEvent(event);
} catch (Exception e) {
System.err.println("[EventBus] 监听器执行异常: " + e.getMessage());
}
}
}
}
}
}
// ========== 具体监听器 ==========
/**
* 日志监听器:记录所有事件
*/
class LoggingListener implements TypedEventListener<Event> {
@Override
public void onEvent(Event event) {
System.out.println(" [日志] " + event.getClass().getSimpleName()
+ " at " + event.getTimestamp()
+ " from " + event.getSource());
}
}
/**
* 用户服务监听器
*/
class UserServiceListener implements TypedEventListener<UserRegisteredEvent> {
@Override
public void onEvent(UserRegisteredEvent event) {
System.out.println(" [用户服务] 发送欢迎邮件给: " + event.getUsername());
System.out.println(" [用户服务] 初始化用户积分账户: " + event.getUsername());
}
}
/**
* 营销服务监听器
*/
class MarketingListener implements TypedEventListener<UserRegisteredEvent> {
@Override
public void onEvent(UserRegisteredEvent event) {
System.out.println(" [营销] 发送新用户优惠券给: " + event.getUsername());
}
}
/**
* 库存服务监听器
*/
class InventoryListener implements TypedEventListener<OrderCreatedEvent> {
@Override
public void onEvent(OrderCreatedEvent event) {
System.out.println(" [库存] 扣减商品库存,订单: " + event.getOrderId());
}
}
/**
* 通知服务监听器
*/
class NotificationListener implements TypedEventListener<PaymentCompletedEvent> {
@Override
public void onEvent(PaymentCompletedEvent event) {
System.out.println(" [通知] 发送支付成功通知,订单: " + event.getOrderId()
+ " 金额: " + event.getAmount());
}
}
// ========== 客户端代码 ==========
public class EventBusDemo {
public static void main(String[] args) {
EventBus eventBus = new EventBus();
// 注册监听器
System.out.println("===== 注册监听器 =====");
eventBus.subscribe(UserRegisteredEvent.class, new UserServiceListener());
eventBus.subscribe(UserRegisteredEvent.class, new MarketingListener());
eventBus.subscribe(OrderCreatedEvent.class, new InventoryListener());
eventBus.subscribe(PaymentCompletedEvent.class, new NotificationListener());
// 发布事件
System.out.println("\n===== 发布用户注册事件 =====");
eventBus.publish(new UserRegisteredEvent("UserService", "张三"));
System.out.println("\n===== 发布订单创建事件 =====");
eventBus.publish(new OrderCreatedEvent("OrderService", "ORD001", 299.0));
System.out.println("\n===== 发布支付完成事件 =====");
eventBus.publish(new PaymentCompletedEvent("PaymentService", "ORD001", 299.0, "支付宝"));
// 取消订阅
System.out.println("\n===== 取消订阅 =====");
eventBus.unsubscribe(UserRegisteredEvent.class, new MarketingListener() {
// 注意:实际使用中需要保存监听器引用
});
}
}六、Java 内置观察者模式
java
import java.util.Observable;
import java.util.Observer;
/**
* Java 9+ 已废弃 java.util.Observable
* 推荐使用自定义实现或 RxJava/Project Reactor
*
* 以下是 Java 8 的用法示例(仅供了解)
*/
// 被观察者(已废弃)
class StockPrice extends Observable {
private String name;
private double price;
public StockPrice(String name, double price) {
this.name = name;
this.price = price;
}
public void setPrice(double newPrice) {
this.price = newPrice;
setChanged(); // 标记状态已改变
notifyObservers(); // 通知所有观察者
}
public String getName() { return name; }
public double getPrice() { return price; }
}
// 观察者(已废弃)
class PriceDisplay implements Observer {
private String name;
public PriceDisplay(String name) {
this.name = name;
}
@Override
public void update(Observable o, Object arg) {
StockPrice stock = (StockPrice) o;
System.out.println(" [" + name + "] " + stock.getName()
+ " 价格更新: " + stock.getPrice());
}
}七、UML 类图
┌──────────────────┐ ┌──────────────────┐
│ Subject │─────────▶│ Observer │
│ (被观察者/主题) │ 通知 │ (观察者) │
│ +attach() │─────────▶│ +update() │
│ +detach() │ └──────────────────┘
│ +notify() │ ▲
└────────┬─────────┘ │ 实现
│ 实现 ┌────────┴────────┐
┌────────┴─────────┐ │ConcreteObserver 1│
│ConcreteSubject │ │ +update() │
│ -state │ └─────────────────┘
│ +getState() │ ┌─────────────────┐
│ +setState() │ │ConcreteObserver 2│
└──────────────────┘ │ +update() │
└─────────────────┘八、优缺点分析
优点
- 松耦合:观察者和被观察者之间没有直接依赖
- 支持广播通信:一个通知可以发送给多个观察者
- 符合开闭原则:新增观察者不需要修改被观察者
- 动态订阅/取消:可以在运行时动态添加或移除观察者
- 实时通知:状态变化时立即通知所有观察者
缺点
- 通知顺序不确定:观察者的执行顺序可能不确定
- 循环依赖:观察者和被观察者之间可能产生循环调用
- 性能问题:观察者过多时,通知所有观察者可能影响性能
- 内存泄漏:如果观察者没有正确取消订阅,可能导致内存泄漏
- 通知丢失:如果观察者在通知过程中抛出异常,可能影响其他观察者
九、使用场景
- GUI 事件处理:按钮点击、键盘输入等事件
- 消息推送:微信公众号、新闻推送
- 数据绑定:MVC 框架中的数据绑定
- Spring 事件机制:ApplicationEvent 和 ApplicationListener
- 消息队列:Kafka、RabbitMQ 等消息中间件
- 股票行情:实时股价更新通知
- 日志系统:多个日志处理器同时处理日志事件
十、Spring 事件机制示例
java
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
/**
* Spring 事件机制就是观察者模式的实现
*/
// 1. 定义事件
class OrderCreatedEvent2 extends ApplicationEvent {
private final String orderId;
public OrderCreatedEvent2(Object source, String orderId) {
super(source);
this.orderId = orderId;
}
public String getOrderId() { return orderId; }
}
// 2. 发布事件
// @Service
// class OrderService {
// @Autowired
// private ApplicationEventPublisher publisher;
//
// public void createOrder(String orderId) {
// // ... 创建订单逻辑
// publisher.publishEvent(new OrderCreatedEvent2(this, orderId));
// }
// }
// 3. 监听事件
// @Component
// class OrderEventHandler {
// @EventListener
// public void handleOrderCreated(OrderCreatedEvent2 event) {
// System.out.println("处理订单创建事件: " + event.getOrderId());
// }
//
// @EventListener(condition = "#event.amount > 100")
// public void handleLargeOrder(OrderCreatedEvent2 event) {
// System.out.println("大额订单: " + event.getOrderId());
// }
// }十一、最佳实践
- 使用弱引用:防止内存泄漏(Java 的 WeakReference)
- 异步通知:对于耗时操作,使用异步方式通知观察者
- 异常处理:在通知观察者时捕获异常,避免一个观察者影响其他观察者
- 事件过滤:让观察者可以只订阅感兴趣的事件类型
- 使用线程安全的集合:在多线程环境下使用 CopyOnWriteArrayList 等线程安全集合
java
// 推荐:使用 CopyOnWriteArrayList 保证线程安全
private final List<Observer> observers = new CopyOnWriteArrayList<>();
// 推荐:异常隔离
public void notifyObservers() {
for (Observer observer : observers) {
try {
observer.update(this);
} catch (Exception e) {
log.error("通知观察者失败", e);
}
}
}总结:观察者模式实现了对象之间的一对多依赖关系,当一个对象状态变化时,所有依赖于它的对象都会得到通知。它是 Java 中最常用的设计模式之一,在 GUI 编程、消息推送、Spring 事件机制等场景中广泛应用。关键在于正确管理观察者的订阅和取消订阅,避免内存泄漏。