观察者模式:当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知依赖它的对象。观察者模式属于行为型模式,又叫发布-订阅模型、模型-视图模式

示例说明:存在一个天气网站,每当天气有更新,都需要通知其他的第三方网站

普通方案设计

  • 接受方需要设计一个 update() 方法,用于接受来自数据源的推送
  • 在天气类 WeatherData 中存放需要推送的对象,在 WeatherData 每次存在数据更新时主动调用接收方的 update() 方法进行推送 ```java // 天气数据 public class WeatherData { // 温度 private float temperature; // 气压 private float pressure; // 湿度 private float humidity; // 需要推送的对象 private CurrentConditions currentConditions;

    public WeatherData(CurrentConditions currentConditions) {

    1. this.currentConditions = currentConditions;

    }

    public float getTemperature() {

    1. return temperature;

    }

    public float getPressure() {

    1. return pressure;

    }

    public float getHumidity() {

    1. return humidity;

    }

    // 数据推送 public void dataChange() {

    1. currentConditions.update(getTemperature(), getPressure(), getHumidity());

    }

    // 数据更新 public void setData(float temperature, float pressure, float humidity) {

    1. this.temperature = temperature;
    2. this.pressure = pressure;
    3. this.humidity = humidity;
    4. // 数据更新后进行数据推送
    5. this.dataChange();

    } }

// 需要推送的对象 public class CurrentConditions { // 温度 private float temperature; // 气压 private float pressure; // 湿度 private float humidity;

  1. // 更新天气情况,由WeatherData进行调用,使用推送模式
  2. public void update(float temperature, float pressure, float humidity) {
  3. this.temperature = temperature;
  4. this.pressure = pressure;
  5. this.humidity = humidity;
  6. // 在更新数据的同时进行输出
  7. display();
  8. }
  9. public void display() {
  10. System.out.println("temperature = " + temperature);
  11. System.out.println("pressure = " + pressure);
  12. System.out.println("humidity = " + humidity);
  13. }

}

  1. 缺点:
  2. - 耦合度高,每次新增一个第三方,都需要修改 WeatherData ,在该类中创建一个对应第三方的公告板对象,然后加入到 dataChange() 进行推送,不利于维护
  3. - 无法动态的新增和删除第三方接入
  4. <a name="4xkD9"></a>
  5. #### 观察者模式
  6. - 抽象主题角色(Subject):抽象目标类,提供了一个保存观察者对象的聚集类和增加、删除观察者对象的方法,以及通知所有观察者的方法
  7. - 具体主题角色(Concrete Subject):具体目标类,实现了抽象目标中的通知方法,当具体主题的内部状态发生改变时,通知所有注册过的观察者对象
  8. - 抽象观察者(Observer):抽象类或接口,包含一个更新自己的抽象方法,当接到具体主题的更改通知时被调用
  9. - 具体观察者(Concrete Observer):实现抽象观察者中定义的抽象方法,以便在得到目标更改通知时更新自身的状态
  10. ![](https://cdn.nlark.com/yuque/0/2020/gif/750131/1607655919201-4ae1f8f2-c506-45aa-ad75-873c835580fd.gif#align=left&display=inline&height=462&margin=%5Bobject%20Object%5D&originHeight=462&originWidth=590&size=0&status=done&style=none&width=590)
  11. Observer 接口:
  12. ```java
  13. public interface Observer {
  14. void update(float temporary,float pressure,float humidity);
  15. }

Subject:

  1. public abstract class Subject {
  2. protected List<Observer> observers;
  3. public Subject() {
  4. this.observers = new ArrayList<>();
  5. }
  6. /**
  7. * 观察者注册
  8. *
  9. * @param observer
  10. */
  11. void registerObserver(Observer observer){
  12. observers.add(observer);
  13. }
  14. /**
  15. * 移除观察者
  16. *
  17. * @param observer
  18. */
  19. void removeObserver(Observer observer){
  20. observers.remove(observer);
  21. }
  22. /**
  23. * 通知
  24. */
  25. abstract void notifyObservers();
  26. }

WeatherData:天气发布类

  1. public class WeatherData extends Subject {
  2. // 温度
  3. private float temperature;
  4. // 气压
  5. private float pressure;
  6. // 湿度
  7. private float humidity;
  8. public float getHumidity() {
  9. return humidity;
  10. }
  11. public float getTemperature() {
  12. return temperature;
  13. }
  14. public float getPressure() {
  15. return pressure;
  16. }
  17. @Override
  18. public void notifyObservers() {
  19. // 向所有的观察者推送数据
  20. observers.forEach(observer -> {
  21. observer.update(getTemperature(),getPressure(),getHumidity());
  22. });
  23. }
  24. // 数据更新
  25. public void setData(float temperature, float pressure, float humidity){
  26. this.temperature = temperature;
  27. this.pressure = pressure;
  28. this.humidity = humidity;
  29. // 数据更新后进行数据推送
  30. notifyObservers();
  31. }
  32. }

Consumer:被推送方,消息接收方

  1. public class Consumer implements Observer {
  2. private final static String prefix = "观察者接受到推送:\n";
  3. @Override
  4. public void update(float temporary, float pressure, float humidity) {
  5. System.out.println(prefix + "temporary = " + temporary);
  6. System.out.println("pressure = " + pressure);
  7. System.out.println("humidity = " + humidity);
  8. }
  9. }

java 提供的工具类

在 java 中,通过 java.util.Observable 类和 java.util.Observer 接口定义了观察者模式,只要实现它们的子类就可以编写观察者模式实例

Observable 类:抽象目标类,内部有一个 Vector 向量,用于保存所有要通知的观察者对象:

  • void addObserver(Observer o) 方法:用于将新的观察者对象添加到向量中
  • void notifyObservers(Object arg) 方法:调用向量中的所有观察者对象的 update() 方法,通知它们数据发生改变。通常越晚加入向量的观察者越先得到通知
  • void setChange() 方法:用来设置一个 boolean 类型的内部标志位,注明目标对象发生了变化。当它为真时,notifyObservers() 才会通知观察者

Observer 接口:是抽象观察者,它监视目标对象的变化,当目标对象发生变化时,观察者得到通知,并调用 void update(Observable o,Object arg) 方法,进行相应的工作。