观察者模式深度解析:从理论到生产实践

在软件开发中,我们经常需要实现"一个对象状态变化,多个对象自动更新"的场景。比如用户注册成功时,需要发送欢迎邮件、赠送积分、记录日志等多个操作。这种一对多的依赖关系,正是观察者模式的典型应用场景。
观察者模式是什么?
观察者模式(Observer Pattern)是一种行为设计模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。当主题对象的状态发生变化时,所有观察者都会收到通知并自动更新。
这里借用建筑监理的比喻:投资方(观察者们)委托建筑师(主题)监督项目进度。每当有建筑进展,建筑师主动通知所有投资方,无需投资方反复询问。
核心角色与实现
观察者模式主要包含四个角色:
- Subject(主题):定义了被观察者对象的基本接口
- ConcreteSubject(具体主题):主题的具体实现,维护观察者列表
- Observer(观察者):定义了观察者对通知做出反应的方法
- ConcreteObserver(具体观察者):实现Observer接口,观察主题并执行相应操作
下面通过一个实际的电商库存管理示例来展示观察者模式的实现:
java
// 观察者接口
public interface InventoryObserver {
void onInventoryChanged(Product product, int newQuantity);
}
// 具体观察者1:库存预警系统
public class InventoryAlert implements InventoryObserver {
@Override
public void onInventoryChanged(Product product, int newQuantity) {
if (newQuantity < 10) {
System.out.println("⚠️ 库存预警: " + product.getName() + " 库存不足,当前数量: " + newQuantity);
}
}
}
// 具体观察者2:订单处理系统
public class OrderProcessor implements InventoryObserver {
@Override
public void onInventoryChanged(Product product, int newQuantity) {
if (newQuantity < 5) {
System.out.println("📦 自动触发补货: " + product.getName() + " 正在向供应商下单...");
}
}
}
// 具体观察者3:价格调整系统
public class PriceAdjuster implements InventoryObserver {
@Override
public void onInventoryChanged(Product product, int newQuantity) {
if (newQuantity > 100) {
System.out.println("💰 库存充足,可以考虑促销降价!");
}
}
}
// 主题接口
public interface InventorySubject {
void addObserver(InventoryObserver observer);
void removeObserver(InventoryObserver observer);
void notifyObservers(Product product, int quantity);
}
// 具体主题:库存管理器
public class InventoryManager implements InventorySubject {
private final List<InventoryObserver> observers = new ArrayList<>();
private final Map<String, Integer> inventory = new HashMap<>();
@Override
public void addObserver(InventoryObserver observer) {
observers.add(observer);
System.out.println("添加观察者: " + observer.getClass().getSimpleName());
}
@Override
public void removeObserver(InventoryObserver observer) {
observers.remove(observer);
System.out.println("移除观察者: " + observer.getClass().getSimpleName());
}
@Override
public void notifyObservers(Product product, int quantity) {
for (InventoryObserver observer : observers) {
observer.onInventoryChanged(product, quantity);
}
}
public void updateStock(String productId, int quantity) {
inventory.put(productId, quantity);
Product product = new Product(productId, "Product-" + productId);
notifyObservers(product, quantity);
}
}
// 使用示例
public class ObserverDemo {
public static void main(String[] args) {
// 创建库存管理器(主题)
InventoryManager manager = new InventoryManager();
// 创建观察者
InventoryAlert alert = new InventoryAlert();
OrderProcessor orderProcessor = new OrderProcessor();
PriceAdjuster priceAdjuster = new PriceAdjuster();
// 注册观察者
manager.addObserver(alert);
manager.addObserver(orderProcessor);
manager.addObserver(priceAdjuster);
System.out.println("\n--- 模拟库存变化 ---");
manager.updateStock("iphone", 3); // 库存不足场景
System.out.println("\n--- 模拟供应商补货 ---");
manager.updateStock("iphone", 50); // 补货后库存正常
System.out.println("\n--- 模拟库存积压 ---");
manager.updateStock("iphone", 150); // 库存积压场景
}
}
运行结果:
diff
添加观察者: InventoryAlert
添加观察者: OrderProcessor
添加观察者: PriceAdjuster
--- 模拟库存变化 ---
⚠️ 库存预警: Product-iphone 库存不足,当前数量: 3
📦 自动触发补货: Product-iphone 正在向供应商下单...
--- 模拟供应商补货 ---
--- 模拟库存积压 ---
💰 库存充足,可以考虑促销降价!
Spring框架中的事件机制

观察者模式在Spring框架中被广泛应用,其事件机制就是典型的实现。Spring的事件机制以ApplicationContext为核心,实现了观察者模式的高级封装:
java
// 自定义事件
public class UserRegisterEvent extends ApplicationEvent {
private final String username;
public UserRegisterEvent(Object source, String username) {
super(source);
this.username = username;
}
public String getUsername() {
return username;
}
}
// 事件监听器(观察者)
@Component
@EventListener
public class EmailSendListener {
@EventListener(classes = UserRegisterEvent.class)
public void sendEmail(UserRegisterEvent event) {
System.out.println("📧 发送欢迎邮件给: " + event.getUsername());
}
}
@Component
@EventListener
public class PointsGrantListener {
@EventListener(classes = UserRegisterEvent.class)
public void grantPoints(UserRegisterEvent event) {
System.out.println("🎁 为新用户 " + event.getUsername() + " 赠送100积分");
}
}
@Component
@EventListener
public class LogRecordListener {
@EventListener(classes = UserRegisterEvent.class)
public void recordLog(UserRegisterEvent event) {
System.out.println("📝 记录用户注册日志: " + event.getUsername());
}
}
// 事件发布者
@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private ApplicationEventPublisher eventPublisher;
@PostMapping("/register")
public ResponseEntity<String> register(@RequestParam String username) {
// 注册用户...
System.out.println("创建用户: " + username);
// 发布事件
UserRegisterEvent event = new UserRegisterEvent(this, username);
eventPublisher.publishEvent(event);
return ResponseEntity.ok("注册成功");
}
}
Spring的事件机制优势:
- 完全解耦:发布者和订阅者互不感知
- 异步支持:支持@Async异步事件处理
- 事务绑定:@TransactionalEventListener支持事务提交后触发
- 条件过滤:@EventListener支持SpEL条件表达式
消息队列中的观察者模式
消息队列系统RabbitMQ本质上也是观察者模式的延伸,但采用了更灵活的发布-订阅模式:
java
// 配置多队列消息监听
@Component
@RabbitListener
public class OrderEventHandler {
// 监听订单创建事件
@RabbitListener(queues = "orderCreated")
public void handleOrderCreated(Order order) {
log.info("订单创建事件: {}", order.getId());
// 1. 减库存
inventoryService.reduceStock(order);
// 2. 发送确认邮件
emailService.sendOrderConfirm(order);
// 3. 记录业务日志
auditService.log(order);
}
// 监听订单支付事件
@RabbitListener(queues = "orderPaid")
public void handleOrderPaid(String orderId) {
log.info("订单支付事件: {}", orderId);
// 1. 更新订单状态
orderService.payOrder(orderId);
// 2. 通知物流系统发货
logisticsService.shipOrder(orderId);
// 3. 更新用户累计消费
userService.addConsumption(orderId);
}
// 监听订单取消事件
@RabbitListener(queues = "orderCancelled")
public void handleOrderCancelled(Order order) {
log.info("订单取消事件: {}", order.getId());
// 1. 恢复库存
inventoryService.restoreStock(order);
// 2. 处理退款
paymentService.refund(order);
// 3. 发送取消通知
notificationService.orderCancelled(order);
}
}
观察者模式 vs 发布-订阅模式

很多人将观察者模式和发布-订阅模式混为一谈,实际上两者有明显区别:
| 特性 | 观察者模式 | 发布-订阅模式 |
|---|---|---|
| 耦合程度 | 相对较强(主题需维护观察者列表) | 完全解耦(通过消息中介) |
| 通信方式 | 同步通信 | 支持异步通信 |
| 分层结构 | 两层(主题-观察者) | 三层(发布者-临时层-订阅者) |
| 消息过滤 | 不支持 | 支持(如topic) |
| 适用场景 | 同一应用中 | 分布式系统 |
观察者模式工作流程

观察者模式的工作流程主要分为以下几个步骤:
- 注册阶段:观察者调用subscrib方法将自己注册到主题
- 状态变更:主题对象的内部状态发生改变
- 通知阶段:主题调用notify方法通知所有注册的观察者
- 更新阶段:观察者收到通知后,调用对象的update方法更新自身状态
应用场景分析

观察者模式广泛应用于以下场景:
1. GUI组件事件监听
java
// JavaFX 按钮点击监听
Button button = new Button("点击我");
button.setOnAction(event -> {
System.out.println("按钮被点击!");
});
2. 实时数据同步
java
// 数据库binlog监听(基于Canal)
@Component
public class DatabaseChangeHandler extends CanalClientEx {
@EventListener
public void handleChange(CanalEntry.EventType change) {
// 同步到ES
elasticsearchService.sync(change);
// 同步到缓存
cacheService.sync(change);
// 通知下游服务
messageService.publish(change);
}
}
实现要点与注意事项
1. 观察者管理
java
// 使用线程安全的集合管理观察者
private final List<Observer> observers =
Collections.synchronizedList(new ArrayList<>());
// 添加观察者
public void addObserver(Observer observer) {
if (observer == null) {
throw new IllegalArgumentException("观察者不能为空");
}
synchronized(observers) {
observers.add(observer);
}
}
// 移除观察者
public void removeObserver(Observer observer) {
observers.remove(observer);
}
2. 防止观察者循环引用
java
public class ConcreteSubject extends Observable {
private boolean changed = false;
@Override
void notifyObservers() {
// 使用锁避免循环引用
synchronized (this) {
if (!changed) {
return;
}
// 复制观察者列表避免并发修改
Observer[] arr = observers.toArray(new Observer[0]);
clearChanged();
for (Observer observer : arr) {
try {
observer.update(this, null);
} catch (Exception e) {
log.warn("通知观察者失败:", e);
}
}
}
}
}
3. 异步通知避免阻塞
java
// 使用线程池异步通知
@Component
public class AsyncEventPublisher {
private final ExecutorService executor = Executors.newCachedThreadPool();
public void publishEventAsync(InventoryEvent event) {
executor.execute(() -> {
try {
notifyObservers(event);
} catch (Exception e) {
log.error("异步事件通知失败", e);
}
});
}
// 优雅关闭
@PreDestroy
public void shutdown() {
executor.shutdown();
}
}
优缺点分析
✅ 优点:
- 降低耦合度:发布者和订阅者相互解耦,易于系统扩展
- 遵守开闭原则:新增观察者时无需修改主题代码
- 实现广播通信:一个主题状态变化,多个观察者同步更新
- 消息传递高效:直接方法调用,性能优于消息队列
❌缺点:
- 观察者数量与性能相关:当观察者数量较多时,通知过程变慢
- 循环依赖风险:观察者和主题之间可能产生循环引用
- 同步通知阻塞所有观察者:一个观察者阻塞会影响整个通知链
观察者模式作为最经典的设计模式之一,它的思想贯穿于各种事件处理和通知机制中。从简单的对象监听,到复杂的分布式事件系统,观察者模式都能发挥其解耦和扩展性的优势。掌握观察者模式,能够帮助我们构建更加灵活、可维护的软件架构。