觀察者模式深度分析

概述

觀察者模式是一種很常用的設計模式,Android中的廣播(Broadcast)就是用觀察者模式設計的,再往大一步,諸如微博這種社交平臺也是用的觀察者模式,觀察者模式亦被稱作發佈-訂閱模式。觀察者模式包含兩個要素:目標對象、觀察者對象。其中,當目標對象的狀態發生改變時,它所依賴的觀察者將立即得到通知,通知攜帶的數據將在消息中心得到處理

內容

1、使用觀察者模式的場景

當一個對象的狀態需要被多個對象瞭解,以保證高度協作。

2、觀察者模式的結構

這裏寫圖片描述

3、觀察者模式的實現

假設有一個氣象監測模型,那麼Observer是需要獲取氣象通知的觀察者,Subject則是氣象主題,作爲被觀察者。

3.1、主題接口

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

主題的三個核心功能即是:註冊觀察者、刪除觀察者、通知觀察者。所以主題接口務必抽象這三個方法。

3.2、觀察者接口

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

當氣象狀態變化時,主題會把相關狀態值作爲方法參數傳送給觀察者。

3.3、顯示接口

這個和觀察者模式無關。

public interface DisplayElement {
    public void display();
}

3.4、實現主題接口

public class WeatherData implements Subject {
    private ArrayList<Observer> observers;

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

    public WeatherData() {
        this.observers = new ArrayList();
    }

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

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

    public void notifyObservers() {
        for (Observer o : observers) {
            o.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();
    }
}

除了實現Subject接口的三方法外,WeatherData中可以有自己特定的方法,用以實現具體的功能,如setMeasurements(),用以改變觀察值。

在實際運用中,當氣象狀態信息真實變化,或狀態需被按時發佈時,這個方法將被調用。

3.5、實現觀察者接口

public class CurrentConditionsDisplay implements Observer, DisplayElement {

    private float temperature;
    private float humidity;

    public CurrentConditionsDisplay() {
    }

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

    @Override
    public void display() {
        Log.d("display", "Current conditions:" + temperature + "F degrees and" + humidity + "% humidity");
    }
}

這裏的觀察者類型是CurrentConditionsDisplay,實現了Observer接口的update()方法,和DisplayElement的display()方法。
從WeatherData類中可以看到,update()方法在WeatherData類型的對象的數據改變時被調用,作爲觀察者的CurrentConditionsDisplay類型對象將獲得最新的數據,接着調用display()展示數據。但觀察者能獲取主題所發佈的新數據的前提是,觀察者綁定了主題,且未被主題刪除。

public class Test {

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

        CurrentConditionsDisplay display = new CurrentConditionsDisplay();
        weatherData.registerObserver(display);
        weatherData.setMeasurements(80, 65, 30.4f);

        //weatherData.removeObserver(display);
    }
}

主通過registerObserver()方法,主題註冊了觀察者,這一步即是訂閱
當主題調用setMeasurements()方法,就會通知觀察者最新消息,這一步即是發佈

4、java 內置的觀察者接口

實現java.util.Observable接口,然後調用兩個方法:
先調用setChanged(),標記狀態已經改變;
然後調用notifyObservers()方法或notifyObservers(Object arg)方法,通知觀察者,如果調用notifyObservers(Object arg)方法,其參數會被傳送給觀察者。

public class WeatherData extends Observable {

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

    public WeatherData() {

    }

    public void measurementsChanged() {
        setChanged();
        notifyObservers();
    }

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

    public float getTemperature() {
        return temperature;
    }

    public float getHumidity() {
        return humidity;
    }

    public float getPressure() {
        return pressure;
    }
}

觀察者實現:

public class CurrentConditionsDisplay implements Observer, DisplayElement {

    private float temperature;
    private float humidity;

    public CurrentConditionsDisplay() {
    }

    @Override
    public void display() {
        Log.d("display", "temperature:" + temperature + ",humidity:" + humidity);
    }

    @Override
    public void update(Observable o, Object arg) {
        if (o instanceof WeatherData) {
            WeatherData weatherData = (WeatherData) o;
            this.temperature = weatherData.getTemperature();
            this.humidity = weatherData.getHumidity();
            display();
        }
    }
}

使用範例:

public class Test {

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

        CurrentConditionsDisplay display = new CurrentConditionsDisplay();
        weatherData.addObserver(display);
        weatherData.setMeasurements(80, 65, 30.4f);

        //weatherData.deleteObserver(display); //刪除觀察者
    }
}

不過是方法名變了。

結語

如果這篇博客幫助到您,您可以完成以下操作:
在網易雲搜索“星河河”->歌手->點擊進入(您將進入我的網易雲音樂人賬號星河河)->關注我->多聽聽我的歌。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章