[head first 設計模式]第二章 觀察者模式

alexhe101發表於2020-11-23

[head first 設計模式]第二章 觀察者模式

假如我們有一個開發需求——建造一個氣象觀測站展示系統。需求方給我們提供了一個WeatherObject物件,能夠自動獲得最新的測量資料。而我們要建立一個應用,有三種佈告版,分別顯示目前的狀況,氣象統計,簡單預報。三種佈告板能即時顯示WeatherObject物件中更新的資料。

​同時,我們需要這是一個可擴充套件的氣象站,可以公佈一組api,好讓其他開發人員寫出自己的佈告板插入此應用中。

​我們首先來看看我們的大致系統框架

1.jpg

我們的工作就算建立一個應用,利用weatherData物件取得資料,並更新佈告板。

根據weatherData原始檔,我們的工作是實現measurementChanged(),當測量資料備妥時,measurementChanged()方法將會被呼叫。

2.jpg

先來看一個可能的實現,

3.jpg

很明顯,這個實現並不妥當。回想第一章的OO原則,會發現我們在針對具體實現程式設計,這會導致當有新需求時我們必須修改程式。同時,更新佈告板的程式碼會經常改變,我們應該儘可能將其封裝。

接下來我們將應用觀察者模式來改進現有設計。

以報紙訂閱為例,我們像某家報社訂閱報紙,只要他們有新報紙初版,就會給派送給訂戶。而訂戶不想要了,就可以取消訂閱。只要報社還在運營,就不斷有人訂閱或者取消訂閱報紙。

出版者+訂閱者=觀察者模式

如果瞭解了報紙訂閱是怎麼回事,觀察者模式也大體如此。出版者即為觀察者模式中的主題(Subject),訂閱者即為觀察者模式中的觀察者(Observer)

4.jpg

定義觀察者模式

觀察者模式定義了物件之間的一對多依賴,這樣一來,當一個物件改變狀態時,它的所有依賴著都會收到通知並自動更新。

類圖如圖所示

5.jpg

觀察者模式提供了一種物件設計,讓主題和觀察者之間鬆耦合。

對於觀察者,主題只關心觀察者實現了Observer介面,主題並不關心觀察者的細節。

任何時候都可以新增觀察者,因為主題唯一以來的東西是一個實現了Observer介面的物件列表,同時,也可以在任何時候刪除觀察者。

改變主題或觀察者中的其中一方,並不會影響到另一方。

設計原則

設計應該儘可能降低互動物件之間的耦合度

依照觀察者模式,得到我們的新設計

6.jpg

當前,我們暫時不用Java內建的觀察者模式,而是自己實現這部分程式碼。

public interface Observer {
    public void update(float temp,float humidity,float pressure);
}

public interface Subject {
    public void notifyObserver();
    public void removeObserver(Observer observer);
    public void registerObserver(Observer observer);

}
public interface DisplayElement {
    public void display();
}

import java.util.ArrayList;
import java.util.List;

public class WeatherData implements Subject {
    private List<Observer> Observers;
    private float temperature;
    private float humidity;
    private float pressure;
    public WeatherData()
    {
        Observers = new ArrayList<Observer>();
    }

    @Override
    public void notifyObserver() {
        for (Observer o:
            Observers ) {
            o.update(temperature,humidity,pressure);
        }
    }

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

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

    public void measurementChanged()
    {
        notifyObserver();
    }

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

public class CurrentConditionsDisplay implements Observer,DisplayElement{
    private float temperature;
    private float humidity;
    private Subject weatherData;
    @Override
    public void display() {
        System.out.println("Current conditions: "+temperature+"F degrees and "+humidity+"% humidity");
    }

    @Override
    public void update(float temp, float humidity, float pressure) {
    this.temperature = temp;
    this.humidity = humidity;
    display();
    }

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


}
public class WeatherStation {
    public static void main(String[] args) {
        WeatherData weatherData = new WeatherData();
        CurrentConditionsDisplay currentConditionsDisplay =
                new CurrentConditionsDisplay(weatherData);
        weatherData.setMeasurements(80,65,30.4f);
    }
}

使用Java內建的觀察者模式

Java api有自帶的觀察者模式,包含Observer介面和Observable類,在使用上更加方便,很多功能已經事先準備好了。如下是我們使用Java內建觀察者模式修改後的設計。

7.jpg

註冊/刪除觀察者

呼叫Observable物件的addObserver方法和deleteObserver方法即可

被觀察者送出通知

首先呼叫setChanged()方法,標記狀態已經改變,後呼叫notifyObservers(),那麼所有觀察者都會呼叫自身的update方法。

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

public class CurrentConditionsDisplay implements Observer,DisplayElement{
    private double temperature;
    private double humidity;
    private Observable observable;
    @Override
    public void display() {
        System.out.println("Current temperature "+temperature+"F degrees and "+humidity+"%humidity");
    }

    public CurrentConditionsDisplay(Observable observable)
    {
        this.observable = observable;
        observable.addObserver(this);
    }

    @Override
    public void update(Observable o, Object arg) {
        if(o instanceof WheatherData)
        {

            temperature = ((WheatherData) o).getTemperature();
            humidity = ((WheatherData) o).getHumidity();
            display();
        }
    }
}
import java.util.ArrayList;
import java.util.List;
import java.util.Observable;

public class WheatherData extends Observable {
    private double temperature;
    private double humidity;
    private double pressure;
    public double getHumidity() {
        return humidity;
    }

    public double getPressure() {
        return pressure;
    }

    public double getTemperature()
    {
        return temperature;
    }
    
    public void setMeasurements(double temperature,double humidity,double pressure)
    {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        setChanged();
        notifyObservers();
    }
}

不幸的是,observable是一個類而不是介面,導致我們難以繼承其他類,同時也無法擁有自己獨特的實現。

在實際使用時,我們需要權衡是使用jdk自帶的觀察者模式亦或是由自己實現。

相關文章