观察者
更新: 6/28/2025 字数: 0 字 时长: 0 分钟
定义
观察者模式 (Observer Pattern) 定义了对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。这种模式又称发布 - 订阅模式,是事件驱动编程的核心模式。
角色
- 主题 (Subject):维护观察者列表,提供注册/注销接口,状态变化时通知观察者
- 具体主题 (ConcreteSubject):实现主题接口,存储具体状态
- 观察者 (Observer):定义更新接口
- 具体观察者 (ConcreteObserver):实现更新接口,维护对主题的引用
- 客户端 (Client):创建主题和观察者对象并建立订阅关系
实现
新闻发布系统
java
// 主题接口
public interface NewsPublisher {
void registerSubscriber(Subscriber subscriber);
void removeSubscriber(Subscriber subscriber);
void notifySubscribers();
}
// 具体主题
public class TechNewsPublisher implements NewsPublisher {
private List<Subscriber> subscribers = new ArrayList<>();
private String latestNews;
public void publishNews(String news) {
this.latestNews = news;
notifySubscribers();
}
@Override
public void registerSubscriber(Subscriber subscriber) {
subscribers.add(subscriber);
}
@Override
public void removeSubscriber(Subscriber subscriber) {
subscribers.remove(subscriber);
}
@Override
public void notifySubscribers() {
for (Subscriber subscriber : subscribers) {
subscriber.update(latestNews);
}
}
}
// 观察者接口
public interface Subscriber {
void update(String news);
}
// 具体观察者
public class MobileSubscriber implements Subscriber {
private String name;
public MobileSubscriber(String name) {
this.name = name;
}
@Override
public void update(String news) {
System.out.printf("[%s 手机端] 收到新闻:%s%n", name, news);
}
}
public class EmailSubscriber implements Subscriber {
private String email;
public EmailSubscriber(String email) {
this.email = email;
}
@Override
public void update(String news) {
System.out.printf("发送邮件至 %s: %s%n", email, news);
}
}
// 使用示例
public class NewsDemo {
public static void main(String[] args) {
TechNewsPublisher publisher = new TechNewsPublisher();
Subscriber mobileUser = new MobileSubscriber("张三");
Subscriber emailUser = new EmailSubscriber("lisi@example.com");
publisher.registerSubscriber(mobileUser);
publisher.registerSubscriber(emailUser);
// 发布新闻
publisher.publishNews("Java 21 正式发布,引入虚拟线程特性");
publisher.publishNews("OpenAI 发布 GPT-5 模型");
// 取消订阅
publisher.removeSubscriber(emailUser);
publisher.publishNews("量子计算机突破 1000 量子比特");
}
}
股票交易系统
java
// 股票数据
public class StockData {
private String symbol;
private double price;
private double volume;
public StockData(String symbol, double price, double volume) {
this.symbol = symbol;
this.price = price;
this.volume = volume;
}
// getters...
}
// 主题接口
public interface StockMarket {
void addObserver(StockObserver observer);
void removeObserver(StockObserver observer);
void notifyObservers();
}
// 具体主题
public class NASDAQ implements StockMarket {
private List<StockObserver> observers = new ArrayList<>();
private Map<String, StockData> stocks = new HashMap<>();
public void updateStock(String symbol, double price, double volume) {
stocks.put(symbol, new StockData(symbol, price, volume));
notifyObservers();
}
@Override
public void addObserver(StockObserver observer) {
observers.add(observer);
}
@Override
public void removeObserver(StockObserver observer) {
observers.remove(observer);
}
@Override
public void notifyObservers() {
for (StockObserver observer : observers) {
observer.update(stocks);
}
}
}
// 观察者接口
public interface StockObserver {
void update(Map<String, StockData> stocks);
}
// 具体观察者
public class Trader implements StockObserver {
private String name;
private Set<String> watchList;
public Trader(String name, String... symbols) {
this.name = name;
this.watchList = new HashSet<>(Arrays.asList(symbols));
}
@Override
public void update(Map<String, StockData> stocks) {
System.out.printf("交易员 %s 收到股票更新:%n", name);
stocks.entrySet().stream()
.filter(entry -> watchList.contains(entry.getKey()))
.forEach(entry -> {
StockData data = entry.getValue();
System.out.printf(" %s: 价格 %.2f, 成交量 %.2f 万%n",
data.getSymbol(), data.getPrice(), data.getVolume());
});
}
}
public class DisplayBoard implements StockObserver {
@Override
public void update(Map<String, StockData> stocks) {
System.out.println("=== 股票行情显示板 ===");
stocks.values().forEach(data ->
System.out.printf("%s: %.2f (%.2f 万手)%n",
data.getSymbol(), data.getPrice(), data.getVolume()));
}
}
// 使用示例
public class StockMarketDemo {
public static void main(String[] args) {
NASDAQ nasdaq = new NASDAQ();
StockObserver trader1 = new Trader("王五", "AAPL", "GOOG");
StockObserver trader2 = new Trader("赵六", "MSFT", "TSLA");
StockObserver display = new DisplayBoard();
nasdaq.addObserver(trader1);
nasdaq.addObserver(display);
// 更新股票
nasdaq.updateStock("AAPL", 175.32, 45.2);
nasdaq.updateStock("MSFT", 332.15, 32.8);
// 添加新观察者
nasdaq.addObserver(trader2);
nasdaq.updateStock("TSLA", 245.67, 78.3);
}
}
优缺点
优点:
- 松耦合设计
- 支持广播通信
- 遵循开闭原则
- 动态订阅关系
- 实时通知机制
- 支持事件驱动架构
缺点:
- 通知顺序不可控
- 可能引起循环依赖
- 过度通知问题
- 内存泄漏风险(观察者未注销)
- 调试困难
- 性能开销(大量观察者)
应用场景
- GUI 事件处理
- 消息队列系统
- 实时数据监控
- 社交媒体通知
- 股票行情系统
- 游戏引擎事件
- 微服务状态同步
- 配置中心动态更新
与其他模式的关系
- 与中介者模式:观察者通过主题协调通信
- 与责任链模式:可组合实现事件过滤链
- 与命令模式:观察者触发命令执行
- 与状态模式:状态变更通知观察者
- 与代理模式:控制观察者访问权限
- 与单例模式:主题可能为单例
- 与发布/订阅模式:观察者是发布/订阅的简化实现
JDK 中的观察者模式
- Java AWT/Swing事件监听
- Java Beans 属性变更监听
- Java.util.Observer/Observable(已弃用)
- Java Reactive Streams (Flow API)
- Spring ApplicationEvent/Listener
- Java EE CDI 事件机制
- Java Logging Handler
- Java NIO.2 WatchService
注意事项
- 避免主题过载(太多观察者)
- 处理通知异常(避免影响其他观察者)
- 使用弱引用防止内存泄漏
- 支持异步通知
- 考虑事件去重机制
- 设计事件对象携带上下文
- 提供优先级机制
- 支持过滤订阅