一、为什么要用观察者模式?
观察者模式(Observer Pattern)是一种行为型设计模式,其核心目的是建立对象之间的依赖关系,使得当一个对象(称为主题 / Subject)的状态发生变化时,所有依赖它的对象(称为观察者 / Observer)都能自动收到通知并更新自身状态。其应用场景包括:
- 事件驱动系统:如 GUI 界面中按钮点击事件的监听。
- 数据更新通知:如股票价格、天气数据的实时更新推送。
- 消息订阅系统:如新闻推送、社交平台的关注订阅功能。
二、观察者模型解决的核心问题
1、解耦对象间的依赖关系
主题与观察者之间通过抽象接口交互,主题无需知道具体观察者的实现,观察者也不依赖主题的具体实现。例如,一个天气数据中心(主题)可以向多个应用(观察者)发送数据,而无需关心应用如何展示数据。
2、支持灵活的扩展
新增观察者时,只需实现统一的接口并注册到主题中,无需修改主题的代码。例如,在新闻系统中新增一个推送渠道(如短信通知),只需添加对应的观察者类。
3、实现动态的一对多通信
当主题状态变化时,所有观察者会被自动通知,无需手动逐个更新。例如,股票价格变化时,所有订阅该股票的用户都会收到提醒。
三、观察者模式的优缺点
优点:
- 高解耦性,主题和观察者彼此独立,双方只需关注自身逻辑,降低了代码耦合度。例如,电商平台的订单系统(主题)更新时,库存系统、物流系统(观察者)可独立更新,互不影响。
- 良好的可扩展性,新增观察者无需修改现有代码,符合 “开闭原则”。例如,在邮件系统中新增 “未读提醒” 功能,只需添加新的观察者类。
- 支持广播通信,主题可同时通知所有观察者,适合需要批量更新的场景。例如,服务器状态监控系统中,一个状态变化可通知所有监控终端。
- 实时性强,状态变化时自动触发通知,适合需要实时响应的场景(如实时聊天、实时数据监控)。
缺点:
- 潜在的性能问题,若观察者数量过多,主题通知所有观察者可能产生性能开销。例如,股票行情系统若有百万级观察者,一次价格更新可能导致卡顿。
- 通知顺序不可控,主题无法保证观察者的接收顺序,若观察者之间存在依赖关系,可能导致逻辑错误。例如,A 观察者需要先于 B 观察者更新,但代码中无法强制顺序。
- 循环依赖风险,若观察者反向修改主题状态,可能导致循环通知(如 A 更新触发 B,B 更新又触发 A),引发系统崩溃。
四、观察者模式实战
4.1 场景:
有个网站,当发布系统公告时需要通知到管理员和用户,使用观察者模式和Spring事件机制实现。
4.2 观察者模式实现
主题:
public interface Subject {
void registerObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers();
}
观察者:
public interface Observer {
void update(String msg);
}
通知:
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
public class Notice implements Subject {
private static final List<Observer> OBSERVERS = new CopyOnWriteArrayList<>();
private String msg;
public Notice() {
}
public Notice(Observer observer) {
registerObserver(observer);
}
@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(msg);
}
}
public void sendMsg(String msg) {
this.msg = msg;
notifyObservers();
}
}
管理员(通过实现Spring InitializingBean 接口添加观察者):
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Service;
@Service
public class Admin implements Observer, InitializingBean {
@Override
public void afterPropertiesSet() {
new Notice(this);
}
@Override
public void update(String msg) {
System.out.println("管理员收到系统公告为:" + msg);
// 给管理员发送站内消息
}
}
用户(通过实现Spring InitializingBean 接口添加观察者):
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Service;
@Service
public class User implements Observer, InitializingBean {
@Override
public void afterPropertiesSet() {
new Notice(this);
}
@Override
public void update(String msg) {
System.out.println("用户收到系统公告为:" + msg);
// 给用户发送站内消息
}
}
客户端:
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/observer")
public class ObserverController {
@RequestMapping("/send")
public String send() {
Notice notice = new Notice();
notice.sendMsg("测试消息");
return " 消息发送成功 success";
}
}
接口请求:
4.3 Spring 事件机制实现
消息发送事件定义:
import org.springframework.context.ApplicationEvent;
public class SendMessageEvent extends ApplicationEvent {
private final String message;
public SendMessageEvent(Object source, String message) {
super(source);
this.message = message;
}
public String getMessage() {
return message;
}
}
管理员监听事件:
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
@Component
public class Administrator implements ApplicationListener<SendMessageEvent> {
@Override
public void onApplicationEvent(SendMessageEvent event) {
String message = event.getMessage();
System.out.println("ApplicationListener 管理员收到消息:" + message);
}
}
用户监听事件:
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
@Component
public class Client implements ApplicationListener<SendMessageEvent> {
@Override
public void onApplicationEvent(SendMessageEvent event) {
String message = event.getMessage();
System.out.println("ApplicationListener 用户收到消息:" + message);
}
}
事件发布:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
@Service
public class SendMessageService {
@Autowired
private ApplicationEventPublisher eventPublisher;
public void sendMessage(String message) {
eventPublisher.publishEvent(new SendMessageEvent(this, message));
}
}
客户端:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/sendMessage")
public class SendMessageController {
@Autowired
private SendMessageService sendMessageService;
@RequestMapping("/send")
public String send() {
sendMessageService.sendMessage("测试消息");
return "ApplicationListener 测试消息发送成功 success";
}
}
接口请求:
五、观察者模式与 Spring 事件机制对比解析
5.1、核心概念对比
维度 | 观察者模式 | Spring 事件机制 |
---|---|---|
定义 | 一种行为设计模式,实现对象间的一对多依赖关系。 | Spring 框架提供的基于事件的发布 - 订阅模型,是观察者模式的企业级实现。 |
核心组件 | 主题(Subject)、观察者(Observer)。 | 事件(ApplicationEvent)、发布者(ApplicationEventPublisher)、监听器(ApplicationListener)。 |
触发方式 | 主题主动调用观察者的更新方法。 | 通过 ApplicationEventPublisher 发布事件,由 Spring 容器管理事件传播。 |
耦合度 | 需手动维护观察者注册 / 注销,代码耦合度较高。 | 基于接口和注解,依赖 Spring 容器,耦合度低。 |
5.2 使用场景对比
场景 | 观察者模式 | Spring 事件机制 |
---|---|---|
GUI 事件处理 | 原生实现(如 Java AWT/Swing) | 不适用(Spring 不直接处理 GUI) |
系统模块间解耦 | 需手动实现,适用于简单场景 | 推荐使用,通过事件隔离业务逻辑 |
异步事件处理 | 需自行实现线程池,复杂度高。 | 支持 @Async 注解,简单易用。 |
跨服务事件通知 | 不支持远程通信 | 结合 Spring Cloud Bus 可实现分布式事件 |
事务管理 | 无法与事务绑定 | 支持 @TransactionalEventListener,确保事件与事务同步 |
5.3、优缺点对比
观察者模式:
优点:
- 实现简单,无需依赖外部框架。
- 轻量级,适用于小型项目。
缺点:
- 手动管理观察者,代码冗余。
- 事件传播阻塞主线程,性能较差。
- 缺乏统一管理,扩展性有限。
Spring 事件机制
优点:
- 依赖注入简化开发,降低耦合。
- 支持异步、事务性事件,功能丰富。
- 与 Spring 生态无缝集成(如 AOP、缓存)。
缺点:
- 依赖 Spring 容器,无法独立使用。
- 事件定义和监听逻辑分散,调试复杂度较高。
– 欢迎点赞、关注、转发、收藏【技术咖啡馆C】,各大平台同名。