设计模式详解------观察者模式
一、模式概述
1. 模式定义
观察者模式(Observer Pattern) 也叫发布-订阅模式(Publish-Subscribe Pattern) ,是一种行为型设计模式。
它定义对象间一种一对多的依赖关系:
- 一:一个主题(被观察者)
- 多:多个观察者
当主题对象的状态发生改变时,所有依赖它的观察者都会收到通知并自动更新。
2. 核心思想
- 一对多依赖,松耦合
- 被观察者只管发布通知,不关心谁在监听
- 观察者只管订阅并响应,不关心谁发出事件
- 状态变化自动传播,无需主动轮询
3. 主要角色
- 抽象主题(Subject)
提供注册、移除、通知观察者的接口 - 具体主题(ConcreteSubject)
维护状态,状态变化时通知所有观察者 - 抽象观察者(Observer)
定义收到通知后的更新接口 - 具体观察者(ConcreteObserver)
实现更新逻辑,处理自身业务 - 客户端(Client)
创建主题与观察者,建立订阅关系
二、适用场景
- 一个对象状态改变,需要自动通知其他多个对象
- 系统需要解耦发布方与订阅方
- 事件驱动、消息广播、状态同步场景
- 框架中的事件机制、监听机制
典型业务场景:
- 订单状态变更通知(用户、物流、库存)
- 商品降价提醒
- 聊天室消息推送
- 配置变更实时生效
- 前端事件监听、Spring 事件模型
三、实战代码实现(模拟订单状态通知)
场景说明
订单状态改变时,需要同时通知:
- 用户(短信/APP推送)
- 物流系统(准备发货)
- 库存系统(扣减完成)
使用观察者模式实现一对多通知。
1. 抽象观察者
java
/**
* 抽象观察者
*/
public interface Observer {
// 收到通知后执行的方法
void update(String orderStatus);
}
2. 具体观察者 A:用户通知
java
public class UserObserver implements Observer {
@Override
public void update(String orderStatus) {
System.out.println("【用户通知】订单状态更新:" + orderStatus + ",已推送APP消息");
}
}
3. 具体观察者 B:物流系统
java
public class LogisticsObserver implements Observer {
@Override
public void update(String orderStatus) {
if ("已支付".equals(orderStatus)) {
System.out.println("【物流系统】准备打包发货");
}
}
}
4. 具体观察者 C:库存系统
java
public class StockObserver implements Observer {
@Override
public void update(String orderStatus) {
if ("已支付".equals(orderStatus)) {
System.out.println("【库存系统】确认扣减库存完成");
}
}
}
5. 抽象主题
java
import java.util.List;
/**
* 抽象主题(被观察者)
*/
public interface Subject {
// 注册观察者
void attach(Observer observer);
// 移除观察者
void detach(Observer observer);
// 通知所有观察者
void notifyObservers();
}
6. 具体主题:订单主题
java
import java.util.ArrayList;
import java.util.List;
/**
* 订单主题:状态变化时通知所有人
*/
public class OrderSubject implements Subject {
private List<Observer> observerList = new ArrayList<>();
private String orderStatus;
// 状态变更
public void setOrderStatus(String orderStatus) {
this.orderStatus = orderStatus;
// 状态变了 → 通知观察者
notifyObservers();
}
@Override
public void attach(Observer observer) {
observerList.add(observer);
}
@Override
public void detach(Observer observer) {
observerList.remove(observer);
}
@Override
public void notifyObservers() {
for (Observer observer : observerList) {
observer.update(orderStatus);
}
}
}
7. 客户端测试
java
public class Client {
public static void main(String[] args) {
// 1. 创建主题(订单)
OrderSubject orderSubject = new OrderSubject();
// 2. 创建观察者
UserObserver userObserver = new UserObserver();
LogisticsObserver logisticsObserver = new LogisticsObserver();
StockObserver stockObserver = new StockObserver();
// 3. 注册订阅
orderSubject.attach(userObserver);
orderSubject.attach(logisticsObserver);
orderSubject.attach(stockObserver);
// 4. 改变订单状态 → 自动通知
System.out.println("===== 订单状态变为:已支付 =====");
orderSubject.setOrderStatus("已支付");
}
}
8. 运行结果
===== 订单状态变为:已支付 =====
【用户通知】订单状态更新:已支付,已推送APP消息
【物流系统】准备打包发货
【库存系统】确认扣减库存完成
四、Java 内置观察者(了解)
Java 提供原生支持:
- 被观察者:
java.util.Observable(类) - 观察者:
java.util.Observer(接口)
缺点:
- Observable 是类,不是接口,无法多继承
- 线程不安全,功能简单
实际开发一般自己实现或使用 Guava、Spring 事件。
五、观察者模式优缺点
优点
- 松耦合:发布者与订阅者互不依赖,可独立扩展
- 动态灵活:可随时增删观察者
- 符合开闭原则:新增观察者无需修改主题
- 广播机制:一次状态变更,多方同步
缺点
- 观察者过多时,通知效率下降
- 循环依赖可能导致死循环
- 观察者只知道主题变化,不知道如何变化
- 调试链路较长
六、经典框架应用(面试高频)
1. Spring Event 事件机制
- ApplicationEvent(事件)
- ApplicationListener(观察者)
- ApplicationContext(发布者)
2. RabbitMQ / Kafka / RocketMQ
消息队列是异步化、分布式版的观察者模式。
3. GUI 事件监听
按钮点击事件 onClickListener 是典型观察者。
4. MyBatis 插件机制
底层使用观察者思想实现拦截与增强。
5. ZooKeeper Watch 机制
节点变化自动通知客户端。
七、观察者模式 VS 责任链模式(面试对比)
| 对比项 | 观察者模式 | 责任链模式 |
|---|---|---|
| 结构 | 一对多 | 一对一链式 |
| 通知方式 | 广播给所有人 | 依次传递,直到被处理 |
| 耦合 | 低 | 中等 |
| 目的 | 同步状态、广播通知 | 分级处理、权限过滤 |
八、总结
- 观察者模式 = 发布 + 订阅
- 核心:一对多、状态自动通知、松耦合
- 被观察者只负责通知,不关心观察者逻辑
- 适合事件驱动、状态同步、消息广播场景
- 是 Spring、消息队列、事件驱动架构的核心思想
一句话记忆:一个主题多监听,状态一变全通知;解耦发布与订阅,事件驱动最常用!