设计模式之管理状态_Observer观察者模式_发送状态变化通知
前言
Github:https://github.com/HealerJean
一、观察者模式(Observer Pattern)
1. 模式概述
观察者模式 是一种 行为型设计模式,它定义了对象之间 一对多的依赖关系,当一个对象(被观察者)的状态发生改变时,所有依赖于它的对象(观察者)都会 自动收到通知并更新。
核心思想:“发布-订阅”机制 —— 被观察者不关心谁在听,只负责广播;观察者不主动拉取,只被动响应。
观察者模式 = 状态驱动 + 自动通知
- 它不是“轮询检查”,而是“事件驱动”——让系统具备 响应式能力。
类比理解:
- 微信公众号:你关注(订阅)某个号,它一发新文章,你就收到推送;
- 天气预报系统:气象站数据更新 → 自动通知所有显示屏、手机 App、网站;
- 股票价格变动:股价变化 → 所有盯盘软件实时刷新。
2. 使用场景
观察者模式适用于以下情况:
- 一个对象的 状态变化需要通知多个其他对象;
- 对象之间存在 松耦合的“发布-订阅”关系;
- 需要 动态增删监听者(如插件系统、事件总线);
- 希望 避免硬编码调用链(如 A 改变 → B/C/D 自动响应)。
典型应用:
- GUI 事件处理(按钮点击 → 多个监听器响应);
- Spring 的
ApplicationEventPublisher; - 消息队列(Producer → Broker → Multiple Consumers);
- 游戏中的成就系统(击杀敌人 → 触发成就 + 计分 + 音效)。
3. 示例程序:用户登录事件通知(优化版)
1)观察者接口 UserObserver
/**
* 观察者接口(Observer)
*/
public interface UserObserver {
void update(User user);
}
2)被观察者类 User
import java.util.ArrayList;
import java.util.List;
/**
* 被观察者(Subject)
* 管理观察者列表,并在状态变化时通知
*/
public class User {
private String name;
private String password;
// 观察者列表(支持多个)
private final List<UserObserver> observers = new ArrayList<>();
// 注册观察者
public void addObserver(UserObserver observer) {
observers.add(observer);
}
// 移除观察者
public void removeObserver(UserObserver observer) {
observers.remove(observer);
}
// 通知所有观察者
private void notifyObservers() {
for (UserObserver observer : observers) {
observer.update(this);
}
}
// 登录操作(状态变更点)
public void login(String name, String password) {
this.name = name;
this.password = password;
System.out.println("正在登陆的用户为:" + name + ",密码为:" + password);
// 状态已更新,通知所有观察者
notifyObservers();
}
// Getter(供观察者读取状态)
public String getName() { return name; }
public String getPassword() { return password; }
}
3)具体观察者
a、日志观察者
public class LoggingObserver implements UserObserver {
@Override
public void update(User user) {
System.out.println("[日志] 监听到用户登录: " + user.getName());
}
}
b、安全审计观察者
public class SecurityAuditObserver implements UserObserver {
@Override
public void update(User user) {
System.out.println("[安全] 记录登录事件: " + user.getName() + " at " + System.currentTimeMillis());
}
}
4)客户端测试 Main
- 支持 任意数量观察者;
- 观察者 无需知道彼此存在;
- 新增观察者(如
EmailObserver)无需修改User类。
/**
* 客户端(Client)
*/
public class Main {
public static void main(String[] args) {
User user = new User();
// 注册多个观察者
user.addObserver(new LoggingObserver());
user.addObserver(new SecurityAuditObserver());
// 触发登录(状态变更)
user.login("healerjean", "password");
}
}
输出:
正在登陆的用户为:healerjean,密码为:password
[日志] 监听到用户登录: healerjean
[安全] 记录登录事件: healerjean at 1700000000000
5)模式角色
- 被观察者 持有观察者集合(非单个);
- 通知时 遍历所有观察者,调用其
update()方法。
| 角色 | 职责 | 示例 |
|---|---|---|
Subject(被观察者/目标) |
管理观察者列表,提供注册/注销接口,状态变更时通知所有观察者 | User / Observable |
Observer(观察者接口) |
定义更新接口,供被观察者回调 | UserObserver |
ConcreteSubject(具体被观察者) |
存储状态,状态变化时触发通知 | User |
ConcreteObserver(具体观察者) |
实现更新逻辑,响应状态变化 | LoggingObserver, EmailObserver |
4. FQA
1)模式优点
- 松耦合:被观察者与观察者之间无直接依赖;
- 支持广播通信:一对多通知天然支持;
- 动态订阅:运行时可增删观察者;
- 符合开闭原则:新增观察者无需修改被观察者。
2)注意事项
- 内存泄漏风险:若未及时
removeObserver,观察者可能无法被GC; - 通知顺序不确定:观察者执行顺序不可控(需额外排序);
- 性能问题:大量观察者或高频通知可能导致卡顿;
- 异常传播:一个观察者抛异常可能中断后续通知(建议 try-catch 隔离)。
3)观察者模式 vs 访问者模式
| 对比维度 | 观察者模式(Observer) | 访问者模式(Visitor) |
|---|---|---|
| 目的 | 状态变化时自动通知依赖对象 | 将操作从数据结构中分离,集中处理 |
| 触发方式 | 被观察者主动通知(事件驱动) | 客户端主动调用访问(请求驱动) |
| 核心关系 | 一对多(1 subject → N observers) | 一对一访问,遍历多个元素 |
| 谁持有谁引用 | 被观察者持有观察者列表 | 元素持有访问者接口;访问者知道所有元素类型 |
| 扩展新行为 | ✅ 极易(加一个 Observer) | ✅ 极易(加一个 Visitor) |
| 扩展新元素 | ✅ 容易 | ❌ 困难(需改所有 Visitor) |
| 典型场景 | 事件系统、消息通知、GUI 监听 | 编译器 AST 遍历、报表导出、结构计算 |
| 关键词 | 监听、通知、订阅 | 访问、遍历、处理 |


