你好,我是风一样的树懒,一个工作十多年的后端专家,曾就职京东、阿里等多家互联网头部企业。公众号“吴计可师”,已经更新了近百篇高质量的面试相关文章,喜欢的朋友欢迎关注点赞
观察者模式分步解析
一、模式定义
观察者模式(Observer Pattern) 是一种行为型设计模式,允许对象(观察者)订阅另一个对象(主题)的状态变化,当主题状态改变时,自动通知所有观察者并触发更新操作。
二、核心组件
| 角色 | 职责 |
|---|---|
| Subject(主题) | 维护观察者列表,提供注册、删除和通知方法。 |
| Observer(观察者) | 定义更新接口,接收主题状态变化通知。 |
| ConcreteSubject(具体主题) | 存储状态,状态变化时通知观察者。 |
| ConcreteObserver(具体观察者) | 实现更新方法,定义收到通知后的具体行为。 |
三、实现步骤
1. 定义观察者接口
public interface Observer {
void update(String message); // 推模式:直接传递数据
// void update(Subject subject); // 拉模式:传递主题引用
}
2. 定义主题接口
public interface Subject {
void registerObserver(Observer o);
void removeObserver(Observer o);
void notifyObservers();
}
3. 实现具体主题
public class NewsAgency implements Subject {
private List<Observer> observers = new ArrayList<>();
private String news;
@Override
public void registerObserver(Observer o) {
observers.add(o);
}
@Override
public void removeObserver(Observer o) {
observers.remove(o);
}
@Override
public void notifyObservers() {
for (Observer o : observers) {
o.update(news); // 通知所有观察者
}
}
public void setNews(String news) {
this.news = news;
notifyObservers(); // 状态变更时自动通知
}
}
4. 实现具体观察者
public class Subscriber implements Observer {
private String name;
public Subscriber(String name) {
this.name = name;
}
@Override
public void update(String news) {
System.out.println(name + " 收到新闻更新: " + news);
}
}
5. 客户端使用
public class Client {
public static void main(String[] args) {
NewsAgency agency = new NewsAgency();
Observer user1 = new Subscriber("张三");
Observer user2 = new Subscriber("李四");
agency.registerObserver(user1);
agency.registerObserver(user2);
agency.setNews("今日头条:Java 21正式发布!");
// 输出:
// 张三 收到新闻更新: 今日头条:Java 21正式发布!
// 李四 收到新闻更新: 今日头条:Java 21正式发布!
agency.removeObserver(user1);
agency.setNews("快讯:Spring 6.1新特性预览");
// 输出:
// 李四 收到新闻更新: 快讯:Spring 6.1新特性预览
}
}
四、模式变体
1. 推模型 vs 拉模型
| 模型 | 实现方式 | 适用场景 |
|---|---|---|
| 推模型 | 主题将详细数据通过update()参数传递 | 观察者需固定数据字段 |
| 拉模型 | 观察者通过主题引用主动获取所需数据 | 观察者需灵活选择数据 |
// 拉模型示例
public interface Observer {
void update(Subject subject);
}
public class NewsAgency implements Subject {
// ...
public String getNews() { return news; }
}
public class Subscriber implements Observer {
@Override
public void update(Subject subject) {
if (subject instanceof NewsAgency) {
String news = ((NewsAgency) subject).getNews();
System.out.println("拉取新闻: " + news);
}
}
}
2. 事件驱动架构
- 自定义事件类型:封装状态变化细节
public class NewsEvent { private String title; private String content; // 构造函数、Getter... } public interface Observer { void onNewsPublished(NewsEvent event); }
五、实际应用场景
-
GUI事件处理
- 按钮点击事件监听
- 窗口大小调整监听
-
消息中间件
- RabbitMQ、Kafka的发布-订阅模型
-
状态监控系统
- 服务器资源使用率监控报警
-
MVVM框架
- 数据绑定与视图自动更新
六、性能优化策略
-
异步通知
public void notifyObservers() { ExecutorService executor = Executors.newCachedThreadPool(); for (Observer o : observers) { executor.submit(() -> o.update(news)); } } -
批量更新
public void setNewsBatch(List<String> newsList) { this.newsList = newsList; notifyObservers(); } -
观察者优先级
public class NewsAgency implements Subject { private PriorityQueue<Observer> observers = new PriorityQueue<>(Comparator.comparingInt(Observer::getPriority)); // ... }
七、与发布-订阅模式对比
| 维度 | 观察者模式 | 发布-订阅模式 |
|---|---|---|
| 耦合度 | 主题与观察者直接交互 | 通过消息代理解耦 |
| 灵活性 | 动态增减观察者 | 支持更复杂的路由规则(主题过滤) |
| 典型实现 | Java内置Observer | Redis Pub/Sub、MQTT协议 |
八、注意事项
-
内存泄漏
- 确保及时调用
removeObserver() - 使用弱引用(WeakReference)包装观察者
public class NewsAgency implements Subject { private List<WeakReference<Observer>> observers = new ArrayList<>(); // 注册时 observers.add(new WeakReference<>(o)) }
- 确保及时调用
-
循环通知
- 避免观察者修改主题状态导致无限递归
-
线程安全
- 使用
CopyOnWriteArrayList实现线程安全列表private List<Observer> observers = new CopyOnWriteArrayList<>();
- 使用
九、Java标准库应用
// 属性变更监听(替代已废弃的Observable)
public class NewsAgency {
private PropertyChangeSupport support = new PropertyChangeSupport(this);
public void addListener(PropertyChangeListener listener) {
support.addPropertyChangeListener(listener);
}
public void setNews(String newValue) {
String oldValue = this.news;
this.news = newValue;
support.firePropertyChange("news", oldValue, newValue);
}
}
// 观察者实现
newsAgency.addListener(evt -> {
System.out.println("新闻变更: " + evt.getNewValue());
});
十、总结
观察者模式通过解耦主题与观察者,实现了松耦合的事件通知机制,其核心价值在于:
- 动态订阅机制:运行时灵活管理观察者
- 广播通信能力:一对多消息高效传递
- 开闭原则支持:新增观察者无需修改主题
在实现时需注意:
- 线程安全与性能优化
- 内存泄漏预防
- 推/拉模型选择
适用于需要实现事件驱动架构或状态监控系统的场景,是现代软件设计中实现组件间通信的基础模式之一。
今天文章就分享到这儿,喜欢的朋友可以关注我的公众号,回复“进群”,可进免费技术交流群。博主不定时回复大家的问题。 公众号:吴计可师