这是 2020 年 7 月整理的设计模式专题第十二篇。观察者模式是”发布-订阅”思想的鼻祖——对象状态一变,所有关注它的对象自动收到通知。MQ、事件监听、消息推送都是它的延伸。
简单说明
观察者模式(Observer Pattern):定义了对象之间的一对多依赖,让多个观察者对象同时监听某一个主题对象。
- 当主题对象发生变化时,它的所有依赖者(观察者)都会收到通知并更新;
- 观察者模式使观察者和被观察者之间建立一个抽象的耦合;
- 观察者模式支持广播通信;
- 应用:监听器,例如 ServletRequestListener。
主题(被观察者)──> 通知 ──> 观察者1
│ ├──> 观察者2
│ └──> 观察者3
(一对多:一个主题,多个观察者,变化即广播)
代码实现
以”课程被提问、老师收到通知”为例:
/**
* 描述:课程问题实体类
*/
@Data
public class Question {
private Question() {
}
public Question(String desc, String username) {
this.desc = desc;
this.username = username;
}
/** 问题描述 */
private String desc;
/** 提问人 */
private String username;
}
/**
* 描述:课程,继承被观察者对象
*/
@Data
public class Course extends Observable {
private String courseName;
public Course(String courseName) {
this.courseName = courseName;
}
public void hasQuestion(Question question) {
setChanged(); // 标记状态已改变
notifyObservers(question); // 广播通知所有观察者
}
}
/**
* 描述:老师,观察者,观察课程是否被提问
*/
public class Teacher implements Observer {
private String teacherName;
public Teacher(String teacherName) {
this.teacherName = teacherName;
}
@Override
public void update(Observable o, Object arg) {
if (o instanceof Course) {
Course course = (Course) o;
Question question = (Question) arg;
System.out.println(teacherName + "老师您好,您的课程" + course.getCourseName() + "被"
+ question.getUsername() + "同学提问:" + question.getDesc());
}
}
}
public class ObserverTest {
public static void main(String[] args) {
Course course = new Course("Java架构师");
Teacher teacher = new Teacher("zhangsan");
course.addObserver(teacher);
course.hasQuestion(new Question("final怎么用?", "李二狗"));
}
}
核心三要素:
- 被观察者继承
Observable:拥有添加/删除观察者、通知的能力; setChanged()+notifyObservers(arg):先标记状态变化,再广播(arg 为传递的数据);- 观察者实现
Observer:实现update(o, arg)接收通知。
注:JDK 的
Observable/Observer在 Java 9 已标记废弃,生产环境更推荐手写观察者接口或使用事件总线/Spring 事件。核心思想不变:主题持有观察者列表,变化时遍历通知。
观察者模式的价值
| 对比项 | 传统轮询 | 观察者模式 |
|---|---|---|
| 获取变化 | 观察者主动反复查询 | 主题主动推送通知 |
| 耦合 | 观察者依赖主题内部细节 | 主题只依赖抽象观察者接口 |
| 扩展 | 新增关注者要改业务代码 | 新增观察者只加一个类 |
抽象耦合:主题(Course)只认识 Observer 接口,不认识具体的 Teacher。新增一种观察者(比如”助教""管理员”)时,主题代码完全不用改——只需实现 Observer 接口并 addObserver。
广播通信:一个主题可以被多个观察者监听,notifyObservers 一次通知所有观察者;同一个观察者也可以监听多个主题。
适用场景与优缺点
适用场景:
- 一个对象的改变需要同时改变其他对象,且不知道具体有多少对象要改变;
- 需要广播通信(一对多通知);
- 事件驱动架构(GUI 事件、MQ 消息、Spring 事件监听)。
优点:
- 观察者与被观察者抽象耦合,易于扩展;
- 支持广播通信;
- 符合开闭原则(新增观察者不改主题)。
缺点:
- 通知顺序不可控,观察者之间可能互相影响;
- 如果观察者处理逻辑复杂,通知链可能拖慢主题(异步可缓解);
- 观察者过多时,内存泄漏风险(忘了解除注册)。
现实中的应用
| 场景 | 说明 |
|---|---|
| GUI 事件监听 | 按钮点击、鼠标移动,ActionListener |
| Spring 事件 | ApplicationEvent + @EventListener 发布订阅 |
| MQ | 生产者发布消息,消费者订阅(观察者思想的分布式版) |
| ServletRequestListener | 请求创建/销毁监听 |
| RxJava / 响应式编程 | 数据流观察者模式 |
小结
- 观察者模式定义一对多依赖,主题变化自动通知所有观察者。
- 实现:被观察者继承/持有观察者列表,
setChanged()+notifyObservers()广播;观察者实现update()。 - 价值:抽象耦合(主题只认接口)、广播通信、符合开闭原则。
- 注意:JDK 的 Observable 已废弃,但思想仍是事件驱动的基础。
- 一句话:状态一变,广而告之——关注我的人都自动收到消息。