設計模式(十九)觀察者模式 Observer

  • 模擬場景:

  甲方提供了一個氣象站的接口,氣象站上面裝有:溫度感應裝置、溼度感應裝置、氣壓感應裝置。編程

  如今咱們是乙方,須要設計一個 WeatherData 對象,從氣象站獲取數據,而且利用這些數據,更新三個佈告板(當前情況、氣象統計、天氣預報)。ide

 

  • 初版解決方案:

  經過簡單地分析,咱們能夠很快肯定一套解決方案:this

  WeatherData 提供一個 measurementsChanged() 方法,當這個方法被調用了,去實時獲取氣象站的數據,而後更新到三個佈告板上。spa

public class BadWeatherData {

    @Getter
    private float temperature;
    @Getter
    private float humidity;
    @Getter
    private float pressure;

    private CurrentConditionsDisplay currentConditionDisplay;
    private StatisticsDisplay statisticsDisplay;
    private ForecastDisplay forecastDisplay;

    public BadWeatherData() {
        // some initialized function for displays
    }

    // We don't care how it be called, we only know is when it is called, we will update displays.
    public void measurementsChanged() {
        // We don't care how it gets data
        float temperature = getTemperature();
        float humidity = getHumidity();
        float pressure = getPressure();

        currentConditionDisplay.update(temperature, humidity, pressure);
        statisticsDisplay.update(temperature, humidity, pressure);
        forecastDisplay.update(temperature, humidity, pressure);
    }
}

 

  • 第一套方案有什麼問題?

  顯然,這是一個擴展性不好的解決方案,它有以下問題:設計

  1. 沒有針對接口編程。(Display 應該事先一個公共的接口)
  2. 若是須要增長或者刪除 Display,都要修改代碼。
  3. 不能動態地增長或者刪除 Display。

 

  • 觀察者模式:

  定義了對象之間的一對多依賴,這樣一來,當一個對象改變狀態時,它的全部依賴者都會收到通知並自動更新。code

  觀察者模式 = 主題+觀察者(Subject + Observer)。server

 

  • 理想的 WeatherData 設計方案:
  1. Display 對象,做爲觀察者,須要實現統一的 Observer 接口。
  2. Observer 接口,具備更新 temperature, humidity, pressure 的能力。
  3. WeatherData 對象,做爲主題,須要實現 Subject 接口。
  4. Subject 接口,維護一個 Oberver 列表,對外提供將 Observer 加入/移除 列表的接口,而且具備通知全部 Observer 數據變化的能力。
  5. Observer 能夠主動 訂閱/取消 Subject。

  

  • UML 類圖(爲了防止混亂,類圖只顯示一個 Display 對象):

 

  • 第二版解決方案:
public interface Subject {

    void registerObserver(Observer observer);
    void removeObserver(Observer observer);
    void notifyObservers();
}
public interface Observer {

    void update(float temperature, float humidity, float pressure);
}
public interface DisplayElement {

    void display();
}
@Data
public final class WeatherData implements Subject {

    private float temperature;
    private float humidity;
    private float pressure;

    private List<Observer> observers = new ArrayList<>();

    public void measurementsChanged() {
        notifyObservers();
    }

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

    @Override
    public void removeObserver(Observer observer) {
        observers.remove(observer);
    }

    @Override
    public void notifyObservers() {
        observers.forEach(observer -> observer.update(temperature, humidity, pressure));
    }
}
public class CurrentConditionsDisplay implements Observer, DisplayElement {

    private float temperature;
    private float humidity;

    private Subject weatherData;

    public CurrentConditionsDisplay(Subject weatherData) {
        this.weatherData = weatherData;
        weatherData.registerObserver(this);
    }

    @Override
    public void display() {
        System.out.println("Current conditions: " + temperature + "F degrees and " + humidity + "% humidity");
    }

    @Override
    public void update(float temperature, float humidity, float pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        display();
    }
}
public class ForecastDisplay implements Observer, DisplayElement {

    private float temperature;
    private float humidity;
    private float pressure;

    private Subject weatherData;

    public ForecastDisplay(Subject weatherData) {
        this.weatherData = weatherData;
        weatherData.registerObserver(this);
    }

    @Override
    public void display() {
        System.out.println("Will show forecast related data");
    }

    @Override
    public void update(float temperature, float humidity, float pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        display();
    }
}
public class StatisticsDisplay implements Observer, DisplayElement {

    private float temperature;

    private Subject weatherData;

    public StatisticsDisplay(Subject weatherData) {
        this.weatherData = weatherData;
        weatherData.registerObserver(this);
    }

    @Override
    public void display() {
        System.out.println("Statistics will show average temperature");
    }

    @Override
    public void update(float temperature, float humidity, float pressure) {
        this.temperature = temperature;
        display();
    }
}
相關文章
相關標籤/搜索