GoF23:Observer-观察者

1、观察者模式

观察者(Observer)

  1. 含义:定义对象之间的一对多依赖,当对象状态改变时,所有依赖者会收到通知并自动更新

    image-20220122140845502

  2. 相关概念

    1. 主题(Subject):被观察对象,可以注册观察者对象列表,当状态发生改变时通知观察者。
    2. 观察者(Observer):感知到主题状态发生改变时,自动调用相应方法。
  3. 说明:遵循开闭原则。

类图

image-20220122160657931

应用

  1. java.util
  2. Swing、GUI框架
  3. JavaBeans
  4. RMI

2、自定义方式实现

需求:已知一台测量气象数据的装置,

要求设计一个 WeatherData 和 Display 对象。

  1. WeatherData:能获取气象站数据,发生数据更新时更新显示板内容。

  2. Display:显示气象数据,且支持自定义显示板。

    image-20220122134618112

分析:WeatherData 是主题,Display 是观察者。

2.1、定义接口

观察者 - Observer

用于更新气象数据

public interface Observer {
    void update(double temperature,
                double humidity,
                double pressure);
}

主题 - Subject

提供注册、删除通知观察者的方法。

public interface Subject {
    void addObserver(Observer o);
    
    void deleteObserver(Observer o);
    
    void notifyObservers();
}

业务接口

业务功能

public interface Display {
    void display();
}

2.2、实现类

观察者 - ConditionsDisplay

实现观察者接口和业务接口

  1. 成员变量

    1. 主题状态:温度、湿度、气压。
    2. 对主题接口的引用。
  2. 接口方法

    1. 观察者方法:update(),更新业务状态。
    2. 业务方法:显示气象信息。
  3. 扩展方法:如 cancelObserver(),主动取消注册。

    public class ConditionsDisplay
        implements Observer, Display {
    
        // 主题状态
        private double temperature;
        private double humidity;
        private double pressure;
        // 主题
        private final Subject subject;
    
        public ConditionsDisplay(Subject subject) {
            this.subject = subject;
            // 注册观察者
            subject.addObserver(this);
        }
    
        @Override
        public void update(double temperature, double humidity, double pressure) {
            this.temperature = temperature;
            this.humidity = humidity;
            this.pressure = pressure;
            display();
        }
    
        @Override
        public void display() {
            System.out.println("=====Current Conditions=====");
            System.out.println("temperature(℃)\t" + temperature);
            System.out.println("humidity(%) \t" + humidity);
            System.out.println("pressure(hPa) \t" + pressure);
        }
    
        // public void cancelObserver(){
        //     subject.deleteObserver(this);
        // }
    }
    

主题 - WeatherData

实现主题接口

  1. 成员变量

    1. 主题状态:温度、湿度、气压。
    2. 观察者列表。
  2. 接口方法:主题方法

    1. 注册观察者
    2. 移除观察者
    3. 通知观察者
  3. 业务方法:更新主题状态(气象),通知观察者。

    public class WeatherData
        implements Subject {
        
    	// 主题状态
        private double temperature;
        private double humidity;
        private double pressure;
    	// 观察者列表
        private List<Observer> observerList;
    	
        public WeatherData() {
            observerList = new ArrayList<>();
        }
    
        @Override
        public void addObserver(Observer o) {
            if (o != null) {
                observerList.add(o);
            }
        }
        @Override
        public void deleteObserver(Observer o) {
            int i = observerList.indexOf(o);
            if (i >= 0) {
                observerList.remove(i);
            }
        }
        @Override
        public void notifyObservers() {
            for (Observer observer : observerList) {
                observer.update(temperature, humidity, pressure);
            }
        }
    
        /**
         * 更新气象数据
         */
        public void updateMeasurements(double temperature, double humidity, double pressure) {
            this.temperature = temperature;
            this.humidity = humidity;
            this.pressure = pressure;
            notifyObservers();
        }
    }
    

3、Java API 实现

数据传递方式

推(push)🔥 拉(pull)
含义 Subject 主动将数据传递给 Observer Observer 主动获取 Subject 的数据
优点 Observer 无需多次获取数据 Observer 可以按需获取数据
缺点 通常只能 push 所有数据,无法按需推送 无法一次性获取所有状态

观察者模式 Java API(支持 push 和 pull)

  • 主题:java.util.Observable
  • 观察者:java.util.Observer

3.1、观察者接口 - Observer

  1. o:传送数据的主题。

  2. arg:数据。

    public interface Observer {
        void update(Observable o, Object arg);
    }
    

3.2、主题类 - Observable

重要方法

  1. changed:标识主题状态是否已改变。

    • setChanged()
    • clearChanged()
    • hasChanged()
  2. notifyObservers():通知观察者(重载方法)

    1. 判断主题状态已改变才通知。

    2. 恢复状态,表示当前通知之后没有其它改变。

    3. 通知所有观察者。

      // pull方式:需要观察者自行获取主题数据
      public void notifyObservers() {
          notifyObservers(null);
      }
      
      // push:主题将指定数据agr发给观察者
      public void notifyObservers(Object arg) {
          Object[] arrLocal;
          // 判断
          synchronized (this) {
              if (!changed){
                  return; 
              }
              arrLocal = obs.toArray();
              // 设值
              clearChanged();
          }
          // 通知
          for (int i = arrLocal.length-1; i>=0; i--)
              ((Observer)arrLocal[i]).update(this, arg);
      }
      

分析

Observable 是 class,而不是 interface。

  1. 只能通过继承 Observable 来成为主题。
  2. Java 单继承限制了 Observable 的复用性,如无法同时继承 Observable 和另一个重要超类。
  3. 违反合成复用原则。
posted @ 2022-01-22 13:32  Jaywee  阅读(206)  评论(0编辑  收藏  举报

👇