一、模式定义
观察者模式属于行为型筹划模式,用于建立对象间的一对多依赖关系。当主题(Subject)状态变革时,所有依赖的观察者(Observer)会自动收到通知并更新。
二、焦点角色
- 维护观察者列表,提供添加/删除观察者的方法
- 定义通知观察者的方法
三、经典实现(以景象站为例)
登录后复制 - // 1. 主题接口
- interface Subject {
- void registerObserver(Observer o);
- void removeObserver(Observer o);
- void notifyObservers();
- }
- // 2. 观察者接口
- interface Observer {
- void update(float temp, float humidity, float pressure);
- }
- // 3. 具体主题实现
- class WeatherData implements Subject {
- private List<Observer> observers = new ArrayList<>();
- private float temperature;
- private float humidity;
- private float pressure;
- @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(temperature, humidity, pressure);
- }
- }
- public void measurementsChanged() {
- notifyObservers();
- }
- public void setMeasurements(float temp, float humidity, float pressure) {
- this.temperature = temp;
- this.humidity = humidity;
- this.pressure = pressure;
- measurementsChanged();
- }
- }
- // 4. 具体观察者实现
- class CurrentConditionsDisplay implements Observer {
- private float temperature;
- private float humidity;
- public CurrentConditionsDisplay(Subject weatherData) {
- weatherData.registerObserver(this);
- }
- @Override
- public void update(float temp, float humidity, float pressure) {
- this.temperature = temp;
- this.humidity = humidity;
- display();
- }
- public void display() {
- System.out.println("Current conditions: " + temperature
- + "°C and " + humidity + "% humidity");
- }
- }
- // 5. 使用示例
- public class WeatherStation {
- public static void main(String[] args) {
- WeatherData weatherData = new WeatherData();
- CurrentConditionsDisplay display = new CurrentConditionsDisplay(weatherData);
-
- weatherData.setMeasurements(25.5f, 65, 1013.1f);
- weatherData.setMeasurements(26.8f, 70, 1012.5f);
- }
- }
复制代码
- 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.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
四、两种通知模型对比
特性
| 推模型(Push)
| 拉模型(Pull)
| 数据传递方式
| 主题主动发送完整数据
| 观察者从主题拉取所需数据
| 实现复杂度
| 观察者大概收到不须要的数据
| 观察者按需获取数据,须要维护主题引用
| 耦合度
| 观察者依赖具体数据结构
| 观察者只需知道主题存在
| 性能考量
| 大概传输冗余数据
| 须要多次调用获取方法
| 拉模型改进示例:
登录后复制 - interface Subject {
- // 新增获取状态的方法
- float getTemperature();
- float getHumidity();
- float getPressure();
- }
- class WeatherData implements Subject {
- // 保持原有接口不变
- // 新增状态获取方法...
- }
- class CurrentConditionsDisplay implements Observer {
- private Subject weatherData;
- public CurrentConditionsDisplay(Subject weatherData) {
- this.weatherData = weatherData;
- weatherData.registerObserver(this);
- }
- @Override
- public void update() {
- this.temperature = weatherData.getTemperature();
- this.humidity = weatherData.getHumidity();
- display();
- }
- }
复制代码
- 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.
五、Java内置支持
登录后复制 - // 已过时,不推荐使用,仅作了解
- import java.util.Observable;
- import java.util.Observer;
- class WeatherData extends Observable {
- public void measurementsChanged() {
- setChanged(); // 必须调用
- notifyObservers(); // 无参为拉模型
- // notifyObservers(data); // 带参为推模型
- }
- }
- class Display implements Observer {
- @Override
- public void update(Observable o, Object arg) {
- if (o instanceof WeatherData) {
- // 拉取数据
- WeatherData wd = (WeatherData) o;
- // 或使用arg参数获取推送数据
- }
- }
- }
复制代码
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
六、模式优劣分析
优势:
- 符合开闭原则:新增观察者无需修改主题
- 运行时动态建立对象关系
- 实现抽象耦合,主题无需知道具体观察者
劣势:
- 通知顺序不可控
- 频繁更新大概影响性能
- 循环依赖风险
七、应用场景
- 跨体系事件通知(如订单状态更新)
- GUI事件处理惩罚(按钮点击监听)
- 及时数据监控(股票价格变动)
- 游戏中的成绩体系解锁
- 分布式配置中央(配置变更通知)
八、高级应用技巧
- 异步观察者:
使用线程池处理惩罚通知,制止壅闭主题线程
登录后复制 - ExecutorService executor = Executors.newCachedThreadPool();
- void notifyObservers() {
- for (Observer o : observers) {
- executor.execute(() -> o.update(...));
- }
- }
复制代码
- 防止失效监听:
使用弱引用(WeakReference)存储观察者,防止内存泄漏
- 保证通知顺序:
使用PriorityQueue实现带优先级的观察者队列
- 跨历程观察者:
联合消息队列(如RabbitMQ、Kafka)实现分布式观察者模式
九、相干模式对比
- 中介者模式 vs 观察者模式
中介者会合处理惩罚对象间通讯,而观察者建立直接订阅关系
- 发布-订阅模式 vs 观察者模式
发布-订阅通过消息代明白耦,观察者是直接通讯
十、最佳实践建议
- 优先使用拉模型,降低耦合度
- 为观察者接口筹划合适的更新粒度
- 考虑使用CopyOnWriteArrayList保证线程安全
- 对于复杂场景,建议使用Guava的EventBus或Spring事件机制
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |