基本概念
观察者模式:一种行为型设计模式,定义了对象之间的一对多依赖关系。当一个对象(主题)的状态发生改变时,所有依赖它的对象(观察者)都会自动收到通知并更新。
核心思想:
- 主题(
Subject
)维护观察者(Observer
)列表
- 主题状态变化时,主动通知所有观察者
- 观察者无需知道其他观察者的存在,只关注主题更新
核心结构
- 主题(
Subject
):定义观察者注册/移除方法,维护观察者列表,状态变化时通知观察者。
- 观察者(
Observer
):定义更新接口,接收主题通知并作出响应。
- 具体主题(
ConcreteSubject
):实现主题接口,维护具体状态,状态变化时通知观察者。
- 具体观察者(
ConcreteObserver
):实现观察者接口,存储与主题相关的状态,响应通知。
示例说明
场景:股票价格监控系统,当股票价格变化时,自动通知所有关注该股票的投资者
- 定义主题接口
1 2 3 4 5 6 7 8 9
| public interface Stock { void attach(Investor investor); void detach(Investor investor); void notifyObservers(); void setPrice(double price); double getPrice(); }
|
- 定义观察者接口,投资者
1 2 3 4 5 6
| public interface Investor { void update(Stock stock); String getName(); }
|
- 具体主题实现,阿里巴巴股票主题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| public class AlibabaStock implements Stock { private double price; private List<Investor> investors = new ArrayList<>(); @Override public void attach(Investor investor) { investors.add(investor); System.out.println(investor.getName() + " 关注了阿里巴巴股票"); } @Override public void detach(Investor investor) { investors.remove(investor); System.out.println(investor.getName() + " 取消关注阿里巴巴股票"); } @Override public void notifyObservers() { for (Investor investor : investors) { investor.update(this); } } @Override public void setPrice(double price) { if (this.price != price) { this.price = price; System.out.println("阿里巴巴股票价格更新为: " + price); notifyObservers(); } } @Override public double getPrice() { return price; } }
|
- 具体观察者实现,普通投资者
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public class OrdinaryInvestor implements Investor { private String name; public OrdinaryInvestor(String name) { this.name = name; } @Override public void update(Stock stock) { System.out.println(name + " 收到通知:阿里巴巴当前价格 " + stock.getPrice()); } @Override public String getName() { return name; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| public class InstitutionalInvestor implements Investor { private String name; public InstitutionalInvestor(String name) { this.name = name; } @Override public void update(Stock stock) { double price = stock.getPrice(); if (price > 200) { System.out.println(name + "(机构)注意到:阿里巴巴股价超过200,考虑卖出"); } else { System.out.println(name + "(机构)注意到:阿里巴巴股价 " + price + ",继续持有"); } } @Override public String getName() { return name; } }
|
- 测试验证
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| public class StockMarket { public static void main(String[] args) { Stock alibaba = new AlibabaStock(); Investor investor1 = new OrdinaryInvestor("张三"); Investor investor2 = new OrdinaryInvestor("李四"); Investor institution = new InstitutionalInvestor("中投公司"); alibaba.attach(investor1); alibaba.attach(investor2); alibaba.attach(institution); alibaba.setPrice(180.5); alibaba.setPrice(205.0); alibaba.detach(investor1); alibaba.setPrice(190.0); } }
|
- 执行结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 张三 关注了阿里巴巴股票 李四 关注了阿里巴巴股票 中投公司 关注了阿里巴巴股票 阿里巴巴股票价格更新为: 180.5 张三 收到通知:阿里巴巴当前价格 180.5 李四 收到通知:阿里巴巴当前价格 180.5 中投公司(机构)注意到:阿里巴巴股价 180.5,继续持有 阿里巴巴股票价格更新为: 205.0 张三 收到通知:阿里巴巴当前价格 205.0 李四 收到通知:阿里巴巴当前价格 205.0 中投公司(机构)注意到:阿里巴巴股价超过200,考虑卖出 张三 取消关注阿里巴巴股票 阿里巴巴股票价格更新为: 190.0 李四 收到通知:阿里巴巴当前价格 190.0 中投公司(机构)注意到:阿里巴巴股价 190.0,继续持有
|
观察者模式有两种模型:推模型 ,拉模型
推模型(Push Model
):
拉模型(Pull Model
)
如下示例
1 2 3 4 5 6 7 8 9 10
|
public interface PullInvestor { void update(); }
public double getPrice();
|
JDK内置观察者实现
Java提供了java.util.Observer
和java.util.Observable
实现观察者模式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
|
import java.util.Observable; import java.util.Observer;
class JdkStock extends Observable { private double price; public void setPrice(double price) { if (this.price != price) { this.price = price; setChanged(); notifyObservers(price); } } public double getPrice() { return price; } }
class JdkInvestor implements Observer { private String name; public JdkInvestor(String name) { this.name = name; } @Override public void update(Observable o, Object arg) { if (o instanceof JdkStock && arg instanceof Double) { double price = (Double) arg; System.out.println(name + " 收到JDK通知:股票价格 " + price); } } public String getName() { return name; } }
|
观察者模式定义一系列对象之间的一对多关系,当一个对象改变状态,它的依赖者都会被通知。观察者模式实现了对象之间的松耦合设计,它们可以交流,但是通常对彼此所知甚少,这里涉及到一个原则:尽量做到交互对象之间的松耦合设计。
类图如下:

注意:JDK的Observable
类存在设计缺陷(如非抽象类、线程不安全),实际开发中更推荐自定义接口实现。
总结
优点:
- 解耦主题与观察者:双方无需知道具体实现,只依赖接口;
- 支持广播通信:主题更新时自动通知所有观察者,无需逐个调用;
- 扩展性好:新增观察者无需修改主题代码,符合开闭原则;
缺点:
- 性能开销:大量观察者时,通知可能成为性能瓶颈
- 循环依赖风险:若观察者反向修改主题状态,可能导致循环通知
- 推模型数据冗余:主题推送的数据可能并非所有观察者都需要
使用场景
- 消息订阅系统:新闻推送、邮件订阅、社交媒体关注,消息通知;
- 系统状态监控:服务器状态监控(CPU、内存变化时通知监控系统),设备传感器数据变化通知(温度、湿度传感器);
- 框架与架构设计:
Spring
的事件机制(ApplicationEvent
和ApplicationListener
),事件总线(EventBus
)模式的核心实现
注意
- 内存泄漏:观察者注册后若未正确移除,可能导致主题持有观察者的强引用,引发内存泄漏(尤其在
Android
中需注意Activity
销毁时移除监听)
- 线程安全:多线程环境下需考虑注册、通知的线程安全(可通过同步或事件队列处理)
- 推/拉模型选择:根据数据量和观察者需求选择合适的通知模式
- 观察者排序:若通知顺序重要,需自定义观察者排序规则