设计模式之观察者模式


定义:

观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。

设计原则

  • 封装变化
  • 多用组合,少用继承
  • 针对接口编程,不针对实现编程
  • 为交互对象之间的松耦合设计而努力

观察者模式提供了一种对象设计,让主题和观察者之间松耦合。

气象站系统

此系统中的三个部分是气象站(获取实际气象数据的物理装置)、WeatherData对象(追踪来自气象站的数据,并更新布告板)和布告板(显示目前天气状况给用户看)。

image.png

工作:利用WeatherData对象取得数据,并更新三个布告板:目前状况、气象统计和天气预报。

定义观察者模式:类图

image.png
利用观察者模式,主题是具有状态的对象, 并且可以控制这些状态。也就是说,有“一个”具有状态的主题。另一方面,观察者使用这些状态,虽然这些状态并不属于他们。有许多的观察者,依赖主题来告诉他们状态何时改变了。这就产生一个关系: “一个”主题对“多个”观察者的关系。
主题只知道观察者实现了某个接口(也就是Observer接口)。主题不需要知道观察者的具体类是谁、做了些什么或其他任何细节。
任 何 时 候 我 们 都 可 以 增 加 新 的 观 察 者 。 因 为 主 题 唯 一 依 赖 的 东 西 是 一 个 实 现Observer接口的对象列表,所以我们可以随时增加观察者。
改变主题或观察者其中一方,并不会影响另一方。因为两者是松耦合的,所以只要他们之间的接口仍被遵守,我们就可以自由地改变他们。

设计气象站

实现气象站

image.png

public interface Subject {
    // 这两个方法都需要一个观察者作为变量,该观察者是用来注册或被删除的。
    void registerObserver(Observer c);
    void removerObserver(Observer o);

    // 当主题状态改变时,这个方法会被调用,以通知所有的观察者。
    void notifyObservers();
}


public interface Observer {
    // 当气象观测值改变时,主题会把这些状态值当作方法的参数,传送给观察者
    void update(float temp, float humidity, float pressure);
}

public interface DisplayElement {
    // 当布告板需要显示时,调用此方法
    void display();
}
public class WeatherData implements Subject{
    private ArrayList<Observer> observers; // ArrayList来纪录观察者
    private float temperature;
    private float humidity;
    private float pressure;
    
    public WeatherData() {
        observers = new ArrayList<>();
    }

    @Override
    public void registerObserver(Observer c) {
        observers.add(c);
    }

    @Override
    public void removerObserver(Observer o) {
        int i = observers.indexOf(o);
        if (i >= 0) {
            observers.remove(i);
        }
    }

    // 把状态告诉每一个观察者。因为观察者都实现了update()
    @Override
    public void notifyObservers() {
        for (int i = 0; i < observers.size(); i++) {
            Observer observer = observers.get(i);
            observer.update(temperature, humidity, pressure);
        }
    }


    // 当从气象站得到更新观测值时,我们通知观察者。
    public void measurementsChanged() {
        notifyObservers();
    }

    public void setMeasurements(float temperature, float humidity, float pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        measurementsChanged();
    }
}
public class CurrentConditionsDisplay implements Observer, DisplayElement {
    private float temperature;
    private float humidity;
    private Subject weatherData;

    // 构造器需要 weatherData对象(也就是主题)作为注册之用。
    public CurrentConditionsDisplay(Subject weatherData) {
        this.weatherData = weatherData;
        weatherData.registerObserver(this);
    }

    // display()方法就只是把最近的温度和湿度显示出来。
    @Override
    public void display() {
        System.out.println("Current conditions:" + temperature
                + "F degrees and " + humidity + "% humidity");
    }

    // 当update()被调用时,我们把温度和湿度保存起来,然后调用display()。
    @Override
    public void update(float temp, float humidity, float pressure) {
        this.temperature = temp;
        this.humidity = humidity;
        display();
    }
}

测试程序

public class WeatherStation {

    public static void main(String[] args) {
        WeatherData weatherData = new WeatherData();

        CurrentConditionsDisplay currentDisplay =
                new CurrentConditionsDisplay(weatherData);
        
        // 建立三个布告板 ,并把WeatherData对象传给它们。
        StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData);
        ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData);
        HeatIndexDisplay heatIndexDisplay = new HeatIndexDisplay(weatherData);

        // 模拟新的气象测量
        weatherData.setMeasurements(80, 65, 30.4f);
        weatherData.setMeasurements(82, 70, 29.2f);
        weatherData.setMeasurements(78, 90, 29.2f);
    }
}

image.png

使用Java内置的观察者模式

image.png

**如何把对象变成观察者 **
实现观察者接口(java.uitl.Observer),然后调用任何Observable对象的addObserver()方法。不想再当观察者时,调用deleteObserver()方法

**可观察者要如何送出通知 **
扩展java.util.Observable接口产生“可观察者”类,然后,需要两个步骤:

  1. 先调用setChanged()方法,标记状态已经改变的事实。
  2. 然后调用两种notifyObservers()方法。

**观察者如何接收通知 **
观察者实现了更新的方法

import java.util.Observable;
import java.util.Observer;

public class WeatherData extends Observable {
    private float temperature;
    private float humidity;
    private float pressure;

    public WeatherData() {  // 不再需要自己存储观察者了
    }

    // 不在需要追踪观察者了,也不需要管理注册与删除(让超类干)。所以把注册、添加、通知的相关代码删除
    public void measurementsChanged() {
        setChanged();       // 在 notifyObservers 之前设置状态
        notifyObservers();
    }

    public void setMeasurements(float temperature, float humidity, float pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        measurementsChanged();
    }

    // “拉”
    public float getTemperature() {
        return temperature;
    }

    public float getHumidity() {
        return humidity;
    }

    public float getPressure() {
        return pressure;
    }
}
import java.util.Observable;//导入Observerable
import java.util.Observer;
//实现java.util.Observer接口
public class CurrentConditionsDisplay implements Observer, DisplayElement {
     Observerable observable;
     private float temperature;
     private float humidity;
     public CurrentConditionsDisplay(Observable observable) {
         // 将Observer当参数,并将CurrentConditionsDisplay对象登记为观察者
         this.observable=observable;
         observable.addObserver(this);
     }
     @Override
     public void display() {
         //显示当前温湿度状况
         System. out. println("Current conditions:"+ temperature+"F degrees and"t humidity +"% humidity");
     }
     @Override
     public void update(Observable obs, Object arg) {
         // TODO 自动生成的方法存根
         if(obs instanceof WeatherData){
             WeatherData weatherData=(WeatherData)obs;
             this.temperature=weatherData.getTemperature();
             this.humidity=weatherData.getHumidity();
             display();
         }
    }
}

注:java.util.Observable的实现有许多问题,限制了它的使用和复用。

Observable是一个“类”,你必须设计一个类继承它。如果某类想同时具有Observable类和另一个超类的行为,就会陷入两难,毕竟Java不支持多重继承。这限制了Observable的复用潜力 。


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM