Java 筹划模式:观察者模式

打印 上一主题 下一主题

主题 979|帖子 979|积分 2937

一、模式定义

     观察者模式属于行为型筹划模式,用于建立对象间的一对多依赖关系。当主题(Subject)状态变革时,所有依赖的观察者(Observer)会自动收到通知并更新。
     二、焦点角色

     

  • Subject(主题)
     

  • 维护观察者列表,提供添加/删除观察者的方法
  • 定义通知观察者的方法
     

  • Observer(观察者接口)
     

  • 定义更新接口,用于接收主题通知
     

  • ConcreteSubject(具体主题)
     

  • 存储具体状态信息
  • 状态改变时触发通知
     

  • ConcreteObserver(具体观察者)
     

  • 实现更新逻辑,保持与主题状态同步
     三、经典实现(以景象站为例)

                                   登录后复制                        
  1. // 1. 主题接口
  2. interface Subject {
  3.     void registerObserver(Observer o);
  4.     void removeObserver(Observer o);
  5.     void notifyObservers();
  6. }
  7. // 2. 观察者接口
  8. interface Observer {
  9.     void update(float temp, float humidity, float pressure);
  10. }
  11. // 3. 具体主题实现
  12. class WeatherData implements Subject {
  13.     private List<Observer> observers = new ArrayList<>();
  14.     private float temperature;
  15.     private float humidity;
  16.     private float pressure;
  17.     @Override
  18.     public void registerObserver(Observer o) {
  19.         observers.add(o);
  20.     }
  21.     @Override
  22.     public void removeObserver(Observer o) {
  23.         observers.remove(o);
  24.     }
  25.     @Override
  26.     public void notifyObservers() {
  27.         for (Observer o : observers) {
  28.             o.update(temperature, humidity, pressure);
  29.         }
  30.     }
  31.     public void measurementsChanged() {
  32.         notifyObservers();
  33.     }
  34.     public void setMeasurements(float temp, float humidity, float pressure) {
  35.         this.temperature = temp;
  36.         this.humidity = humidity;
  37.         this.pressure = pressure;
  38.         measurementsChanged();
  39.     }
  40. }
  41. // 4. 具体观察者实现
  42. class CurrentConditionsDisplay implements Observer {
  43.     private float temperature;
  44.     private float humidity;
  45.     public CurrentConditionsDisplay(Subject weatherData) {
  46.         weatherData.registerObserver(this);
  47.     }
  48.     @Override
  49.     public void update(float temp, float humidity, float pressure) {
  50.         this.temperature = temp;
  51.         this.humidity = humidity;
  52.         display();
  53.     }
  54.     public void display() {
  55.         System.out.println("Current conditions: " + temperature
  56.             + "°C and " + humidity + "% humidity");
  57.     }
  58. }
  59. // 5. 使用示例
  60. public class WeatherStation {
  61.     public static void main(String[] args) {
  62.         WeatherData weatherData = new WeatherData();
  63.         CurrentConditionsDisplay display = new CurrentConditionsDisplay(weatherData);
  64.         
  65.         weatherData.setMeasurements(25.5f, 65, 1013.1f);
  66.         weatherData.setMeasurements(26.8f, 70, 1012.5f);
  67.     }
  68. }
复制代码
      

  • 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)
数据传递方式
主题主动发送完整数据
观察者从主题拉取所需数据
实现复杂度
观察者大概收到不须要的数据
观察者按需获取数据,须要维护主题引用
耦合度
观察者依赖具体数据结构
观察者只需知道主题存在
性能考量
大概传输冗余数据
须要多次调用获取方法
          拉模型改进示例:
                                   登录后复制                        
  1. interface Subject {
  2.     // 新增获取状态的方法
  3.     float getTemperature();
  4.     float getHumidity();
  5.     float getPressure();
  6. }
  7. class WeatherData implements Subject {
  8.     // 保持原有接口不变
  9.     // 新增状态获取方法...
  10. }
  11. class CurrentConditionsDisplay implements Observer {
  12.     private Subject weatherData;
  13.     public CurrentConditionsDisplay(Subject weatherData) {
  14.         this.weatherData = weatherData;
  15.         weatherData.registerObserver(this);
  16.     }
  17.     @Override
  18.     public void update() {
  19.         this.temperature = weatherData.getTemperature();
  20.         this.humidity = weatherData.getHumidity();
  21.         display();
  22.     }
  23. }
复制代码
      

  • 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内置支持

                                   登录后复制                        
  1. // 已过时,不推荐使用,仅作了解
  2. import java.util.Observable;
  3. import java.util.Observer;
  4. class WeatherData extends Observable {
  5.     public void measurementsChanged() {
  6.         setChanged();  // 必须调用
  7.         notifyObservers();  // 无参为拉模型
  8.         // notifyObservers(data);  // 带参为推模型
  9.     }
  10. }
  11. class Display implements Observer {
  12.     @Override
  13.     public void update(Observable o, Object arg) {
  14.         if (o instanceof WeatherData) {
  15.             // 拉取数据
  16.             WeatherData wd = (WeatherData) o;
  17.             // 或使用arg参数获取推送数据
  18.         }
  19.     }
  20. }
复制代码
      

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
                       六、模式优劣分析

     优势:
     

  • 符合开闭原则:新增观察者无需修改主题
  • 运行时动态建立对象关系
  • 实现抽象耦合,主题无需知道具体观察者
     劣势:
     

  • 通知顺序不可控
  • 频繁更新大概影响性能
  • 循环依赖风险
     七、应用场景

     

  • 跨体系事件通知(如订单状态更新)
  • GUI事件处理惩罚(按钮点击监听)
  • 及时数据监控(股票价格变动)
  • 游戏中的成绩体系解锁
  • 分布式配置中央(配置变更通知)
     八、高级应用技巧

     

  • 异步观察者
    使用线程池处理惩罚通知,制止壅闭主题线程
                                   登录后复制                        
  1. ExecutorService executor = Executors.newCachedThreadPool();
  2. void notifyObservers() {
  3.     for (Observer o : observers) {
  4.         executor.execute(() -> o.update(...));
  5.     }
  6. }
复制代码
      

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
                       

  • 防止失效监听
    使用弱引用(WeakReference)存储观察者,防止内存泄漏
  • 保证通知顺序
    使用PriorityQueue实现带优先级的观察者队列
  • 跨历程观察者
    联合消息队列(如RabbitMQ、Kafka)实现分布式观察者模式
     九、相干模式对比

     

  • 中介者模式 vs 观察者模式
    中介者会合处理惩罚对象间通讯,而观察者建立直接订阅关系
  • 发布-订阅模式 vs 观察者模式
    发布-订阅通过消息代明白耦,观察者是直接通讯
     十、最佳实践建议

     

  • 优先使用拉模型,降低耦合度
  • 为观察者接口筹划合适的更新粒度
  • 考虑使用CopyOnWriteArrayList保证线程安全
  • 对于复杂场景,建议使用Guava的EventBus或Spring事件机制

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

熊熊出没

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表