1. 概述
本教程将介绍观察者模式,并探讨几种Java实现方案。观察者模式是行为型设计模式中非常实用的一个,掌握它能有效解耦对象间的通信。
2. 什么是观察者模式?
观察者模式定义了对象间的一对多依赖关系:当一个对象(被观察者)状态改变时,所有依赖它的对象(观察者)都会收到通知并自动更新。
举个栗子:新闻机构(被观察者)收到新闻时,会通知所有订阅的频道(观察者)。新闻更新改变了新闻机构的状态,进而触发频道更新。
手动实现方案
被观察者实现
public class NewsAgency {
private String news;
private List<Channel> channels = new ArrayList<>();
public void addObserver(Channel channel) {
this.channels.add(channel);
}
public void removeObserver(Channel channel) {
this.channels.remove(channel);
}
public void setNews(String news) {
this.news = news;
for (Channel channel : this.channels) {
channel.update(this.news);
}
}
}
关键点:被观察者必须维护观察者列表(channels
),状态变化时遍历调用观察者的update()
方法。
观察者实现
public interface Channel {
public void update(Object o);
}
public class NewsChannel implements Channel {
private String news;
@Override
public void update(Object news) {
this.setNews((String) news);
}
// standard getter and setter
}
使用示例
NewsAgency observable = new NewsAgency();
NewsChannel observer = new NewsChannel();
observable.addObserver(observer);
observable.setNews("突发新闻");
assertEquals(observer.getNews(), "突发新闻");
3. 使用Observer
接口实现
Java核心库提供了预定义的Observer
接口,能简化实现过程。
观察者实现
public class ONewsChannel implements Observer {
private String news;
@Override
public void update(Observable o, Object news) {
this.setNews((String) news);
}
// standard getter and setter
}
被观察者实现
public class ONewsAgency extends Observable {
private String news;
public void setNews(String news) {
this.news = news;
setChanged(); // 标记状态已改变
notifyObservers(news); // 触发通知
}
}
优势:无需手动维护观察者列表,Observable
类已内置addObserver()
和deleteObserver()
方法。
使用示例
ONewsAgency observable = new ONewsAgency();
ONewsChannel observer = new ONewsChannel();
observable.addObserver(observer);
observable.setNews("科技新闻");
assertEquals(observer.getNews(), "科技新闻");
⚠️ 重要提醒:Observer
接口在Java 9已被废弃!主要缺陷:
Observable
是类而非接口,无法利用多继承- 子类可能覆盖同步方法破坏线程安全
4. 使用PropertyChangeListener
实现
这是当前推荐方案,通过JavaBeans的属性变更机制实现。
被观察者实现
public class PCLNewsAgency {
private String news;
private PropertyChangeSupport support;
public PCLNewsAgency() {
support = new PropertyChangeSupport(this);
}
public void addPropertyChangeListener(PropertyChangeListener pcl) {
support.addPropertyChangeListener(pcl);
}
public void removePropertyChangeListener(PropertyChangeListener pcl) {
support.removePropertyChangeListener(pcl);
}
public void setNews(String value) {
support.firePropertyChange("news", this.news, value);
this.news = value;
}
}
核心机制:通过PropertyChangeSupport
管理观察者,firePropertyChange()
触发通知。
观察者实现
public class PCLNewsChannel implements PropertyChangeListener {
private String news;
@Override
public void propertyChange(PropertyChangeEvent evt) {
this.setNews((String) evt.getNewValue());
}
// standard getter and setter
}
使用示例
PCLNewsAgency observable = new PCLNewsAgency();
PCLNewsChannel observer = new PCLNewsChannel();
observable.addPropertyChangeListener(observer);
observable.setNews("财经快讯");
assertEquals(observer.getNews(), "财经快讯");
✅ 优势对比:
- 类型安全:通过
PropertyChangeEvent
传递变更详情 - 线程安全:
PropertyChangeSupport
内置同步机制 - 灵活性:支持属性粒度的监听控制
5. 过期监听器问题
所有上述实现都存在内存泄漏风险:如果观察者未显式注销,即使不再使用仍会被被观察者强引用持有。
解决方案
使用WeakReference
弱引用管理观察者:
private List<WeakReference<Channel>> observers = new ArrayList<>();
public void addObserver(Channel channel) {
observers.add(new WeakReference<>(channel));
}
效果:当观察者对象不再被其他地方引用时,GC会自动回收,避免内存泄漏。
6. 结论
观察者模式在Java中有三种主流实现方式:
实现方案 | 推荐度 | 特点 |
---|---|---|
手动实现 | ⭐⭐ | 完全可控但代码量大 |
Observer 接口 |
❌ | 已废弃,存在设计缺陷 |
PropertyChangeListener |
✅ | 类型安全、线程安全、推荐使用 |
最佳实践建议:
- 优先使用
PropertyChangeListener
- 注意观察者注销,避免内存泄漏
- 对于高频通知场景,考虑异步通知机制
完整代码示例可在GitHub仓库查看。