1. 观察者模式核心概念解析
观察者模式是行为型设计模式中最常用的模式之一,它定义了对象间一对多的依赖关系。当被观察对象(Subject)状态发生变化时,所有依赖于它的观察者(Observer)都会自动收到通知并更新。这种模式在事件驱动系统、消息队列、GUI事件处理等场景中广泛应用。
设计模式初学者常犯的错误是将观察者模式与发布-订阅模式混为一谈。虽然两者都实现了对象间的解耦,但发布-订阅模式通常引入中间件作为消息代理,而观察者模式是直接通信。
在Java标准库中,java.util.Observable类和java.util.Observer接口提供了观察者模式的简单实现。但自Java 9起这些API已被标记为过时,因为开发者更倾向于实现自定义的观察者模式以获得更大灵活性。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 推模型与拉模型实现对比
2.1 推模型实现方案
推模型(Push Model)的特点是Subject将变更的详细信息主动推送给Observer。下面是一个完整的Java实现示例:
java复制// 观察者接口
interface PushObserver {
void update(String data); // 直接接收数据
}
// 具体观察者
class ConcretePushObserver implements PushObserver {
@Override
public void update(String data) {
System.out.println("收到数据更新: " + data);
// 直接使用推送过来的数据
}
}
// 主题接口
interface PushSubject {
void registerObserver(PushObserver o);
void removeObserver(PushObserver o);
void notifyObservers();
}
// 具体主题
class ConcretePushSubject implements PushSubject {
private List<PushObserver> observers = new ArrayList<>();
private String state;
public void setState(String state) {
this.state = state;
notifyObservers();
}
@Override
public void registerObserver(PushObserver o) {
observers.add(o);
}
@Override
public void removeObserver(PushObserver o) {
observers.remove(o);
}
@Override
public void notifyObservers() {
for (PushObserver observer : observers) {
observer.update(state); // 推送当前状态
}
}
}
推模型的优势在于:
- 实时性强,Observer能立即获取最新数据
- 减轻Observer的查询负担
- 适合数据量小的场景
但缺点也很明显:
- Subject需要了解Observer需要哪些数据
- 可能推送Observer不需要的数据,造成资源浪费
- 当数据结构变化时,需要修改所有Observer接口
2.2 拉模型实现方案
拉模型(Pull Model)中,Subject只通知Observer状态已改变,Observer根据需要主动从Subject拉取数据:
java复制// 观察者接口
interface PullObserver {
void update(); // 不包含具体数据
}
// 主题接口
interface PullSubject {
String getState(); // 提供状态获取方法
void registerObserver(PullObserver o);
void removeObserver(PullObserver o);
void notifyObservers();
}
// 具体实现
class ConcretePullSubject implements PullSubject {
private List<PullObserver> observers = new ArrayList<>();
private String state;
public void setState(String state) {
this.state = state;
notifyObservers();
}
@Override
public String getState() {
return state;
}
// 其他方法实现与推模型类似...
}
class ConcretePullObserver implements PullObserver {
private PullSubject subject;
public ConcretePullObserver(PullSubject subject) {
this.subject = subject;
}
@Override
public void update() {
String data = subject.getState(); // 主动拉取数据
System.out.println("拉取到数据更新: " + data);
}
}
拉模型的优势包括:
- Subject不需要知道Observer需要什么数据
- Observer可以按需获取数据,避免不必要传输
- 接口更稳定,数据结构变化影响小
但缺点也很明显:
- 实时性稍差,Observer需要二次查询
- 增加了Observer的复杂性
- 可能造成Subject的查询压力
3. 两种模型的性能对比与适用场景
3.1 性能对比指标
| 指标 | 推模型 | 拉模型 |
|---|---|---|
| 网络带宽占用 | 高(传输完整数据) | 低(仅通知) |
| CPU消耗 | Subject端高 | Observer端高 |
| 实时性 | 高 | 中等 |
| 耦合度 | 较高(需知道数据结构) | 较低 |
| 扩展性 | 较差 | 较好 |
3.2 典型应用场景选择
适合推模型的场景:
- 数据量小的实时通知系统(如股票价格变动)
- Observer处理能力有限的场景
- 数据结构稳定的系统
适合拉模型的场景:
- 数据量大的系统(如新闻订阅)
- Observer需要差异化数据的场景
- 需要降低耦合度的分布式系统
在实际项目中,我通常会采用混合模式:关键数据使用推模型保证实时性,大块数据或可选数据使用拉模型按需获取。例如在电商订单系统中,订单状态变更推送给用户,而订单详情则让用户端按需拉取。
4. Java中的高级实现技巧
4.1 使用泛型增强类型安全
java复制interface Observer<T> {
void update(T data);
}
class Subject<T> {
private List<Observer<T>> observers = new ArrayList<>();
public void notifyObservers(T data) {
for (Observer<T> o : observers) {
o.update(data);
}
}
// 其他方法...
}
4.2 异步通知实现
java复制class AsyncSubject extends Subject {
private ExecutorService executor = Executors.newCachedThreadPool();
@Override
public void notifyObservers(Object data) {
for (Observer o : observers) {
executor.submit(() -> o.update(data));
}
}
}
4.3 防止通知循环
java复制class SafeSubject extends Subject {
private boolean notifying = false;
@Override
public void notifyObservers(Object data) {
if (notifying) return;
try {
notifying = true;
super.notifyObservers(data);
} finally {
notifying = false;
}
}
}
5. 实际项目中的经验教训
-
内存泄漏问题:Observer忘记注销导致的内存泄漏是最常见问题。解决方案:
- 使用WeakReference存储Observer
- 明确生命周期管理
- 添加自动清理机制
-
性能优化:当Observer数量庞大时:
- 分批通知
- 使用线程池
- 考虑事件合并(如Android的VSYNC机制)
-
调试技巧:
java复制// 调试用Subject class DebugSubject extends Subject { @Override public void notifyObservers(Object data) { System.out.println("Notifying " + observers.size() + " observers"); super.notifyObservers(data); } } -
测试策略:
- 验证通知顺序是否可控
- 测试高频通知场景
- 模拟Observer处理超时情况
6. 与其他模式的协作
-
与中介者模式结合:当观察者数量多、关系复杂时,可以引入中介者来管理通知逻辑。
-
与责任链模式结合:实现通知的链式处理,每个Observer决定是否继续传递通知。
-
与装饰器模式结合:为Observer添加额外功能,如日志记录、性能监控等。
在Spring框架中,ApplicationEvent机制就是观察者模式的典型实现,支持同步和异步事件发布,并提供了丰富的扩展点。
