《HeadFirst設計模式》第二章觀察者模式-讀書筆記

《HeadFirst設計模式》第二章觀察者模式-讀書筆記

案例代碼連接:github.com/rainweb521/…

1. 背景

此次的引子是關於氣象站的應用,案例中要創建一個應用,有三種天氣預報的展示形式,使用一個WeatherObject對象得到最新測量到的天氣數據,而後對三個佈告板進行實時更新。而且之後可能會新加入其餘的佈告板,須要系統有很高的擴展性。WeatherData對象知道如何跟物理氣象站聯繫,以取得更新的數據。WeatherData對象會隨即更新三個佈告板的顯示:目前情況(溫度、溼度、氣壓)、氣象統計和天氣預報。git

這是WeatherData類github

WeatherData
getTemperature( )
getHumidity( ) 
getPressure( ) 
measure- mentsChanged( )

/*  
* 一旦氣象測量更新,此方法會被調用 
*/
public void measurementsChanged() {
// 你的代碼加在這裏
}
複製代碼

2. 需求分析

2.1 WeatherData類具備getter方法,能夠取得三個測量值:溫度、溼度與氣壓。

2.2 當新的測量數據備妥時,measurementsChanged()方法就會被調用(咱們不在意此方法是如何被調用的,咱們只在意它被調用了)。

2.3 咱們須要實現三個使用天氣數據的佈告板:「目前情況」佈告、「氣象統計」佈告、「天氣預報」佈告。一旦WeatherData有新的測量,這些佈告必須立刻更新。

2.4 此係統必須可擴展,讓其餘開發人員創建定製的佈告板, 用戶能夠爲所欲爲地添加或刪除任何佈告板。目前初始的佈告板有三類:「目前情況」佈告、「氣象統計」佈告、「天氣預報」佈告。

3.開始幹活

3.1 先來試一下

public class WeatherData {
// 實例變量聲明    
public void measurementsChanged() {         
  float temp = getTemperature();         
  float humidity = getHumidity();         
  float pressure = getPressure();         
  currentConditionsDisplay.update(temp, humidity, pressure);         
  statisticsDisplay.update(temp, humidity, pressure);         
  forecastDisplay.update(temp, humidity, pressure);     
}     
// 這裏是其餘WeatherData方法}
複製代碼

這是最簡單的實現辦法,很快就能夠將數據完成更新,可是若是從開發原則上考慮,這樣的代碼太過於粗糙,三個update方法雖然很少,但若是後期再增長佈告板,update方法的調用將會一直增長,不論增長仍是刪除佈告板都會修改程序。web

這段程序就是針對具體實現編程,而不是針對接口編程,咱們須要將類似的地方封裝起來。編程

4.開始咱們的觀察者模式

4.1 認識觀察者模式

咱們看看報紙和雜誌的訂閱是怎麼回事:設計模式

  1. 報社的業務就是出版報紙。bash

  2. 向某家報社訂閱報紙,只要他們有新報紙出版,就會給你送來。ui

    只要你是他們的訂戶,你就會一直收到新報紙。this

  3. 當你不想再看報紙的時候,取消訂閱,他們就不會再送新報紙來。spa

  4. 只要報社還在運營,就會一直有人(或單位)向他們訂閱報紙或取消訂閱報紙。設計

定義觀察者模式

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

觀察者模式定義了一系列對象之間的一對多關係。

當一個對象改變狀態,其餘依賴者都會收到通知。

4.2 鬆耦合的威力

當兩個對象之間鬆耦合,它們依然能夠交互,可是不太清楚彼此的細節。 觀察者模式提供了一種對象設計,讓主題和觀察者之間鬆耦合。

關於觀察者的一切,主題只知道觀察者實現了某個接口(也就是Observer接口)。主題不須要知道觀察者的具體類是誰、作了些什麼或其餘任何細節。 任 何 時 候 我 們 都 可 以 增 加 新 的 觀 察 者 。 因 爲 主 題 惟 一 依 賴 的 東 西 是 一 個 實 現Observer接口的對象列表,因此咱們能夠隨時增長觀察者。事實上,在運行時咱們能夠用新的觀察者取代現有的觀察者,主題不會受到任何影響。一樣的,也能夠在任什麼時候候刪除某些觀察者。

改變主題或觀察者其中一方,並不會影響另外一方。由於二者是鬆耦合的,因此只要他們之間的接口仍被遵照,咱們就能夠自由地改變他們。

設計原則:爲了交互對象之間的鬆耦合設計而努力

5.從新開始開發

5.1 設計氣象站

5.2 實現氣象站

依照前面的類圖,開始編寫氣象站的代碼

public interface Subject {
//    用來註冊或刪除觀察者
    public void registerObserver(Observer o);
    public void removeObserver(Observer o);
//    當主題狀態改變時,調用此方法通知全部觀察者
    public void notifyObservers();
}

複製代碼
public interface Observer {
//    但氣象觀測值改變時,主題會把這些狀態值看成方法的參數,傳遞給觀察者
    public void update(float temp,float humidity,float pressure);
}

複製代碼
public interface DisplayElement {
//    DisplayElement接口只包含了一個方法, 也就是display()。當佈告板須要顯示時, 調用此方法。
    public void display();
}

複製代碼
//WeatherData如今實現了Subject接口。
public class WeatherData implements Subject {

//    咱們加上一個ArrayList來紀錄觀察者,此ArrayList是在構造器中創建的。
    private ArrayList observers;
    private float temperature;
    private float humidity;
    private float pressure;

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

//    當註冊觀察者時,咱們只要把它加到ArrayList的後面便可。
    public void registerObserver(Observer o) {
        observers.add(o);
    }

//    當觀察者想取消註冊,咱們把它從ArrayList中刪除便可。
    public void removeObserver(Observer o) {
        int i = observers.indexOf(o);
        if (i>=0){
            observers.remove(o);
        }
    }

//    咱們把狀態告訴每個觀察者。
    public void notifyObservers() {
        for (int i = 0 ;i < observers.size(); i++){
            Observer 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();
    }
}
複製代碼
//佈告板實現了Observer接口,因此能夠從WeatherData對象中得到改變
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);
    }


    public void display() {
        System.out.println(temperature+""+humidity);
    }

//    當update()被調用時,咱們把溫度和溼度保存起來, 而後調用display()。
    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 currentConditions = new CurrentConditionsDisplay(weatherData);
        weatherData.setMeasurements(80,65,20);
    }
}

複製代碼

運行WeatherStation能夠獲得結果,由此整個代碼編寫完成。

相關文章
相關標籤/搜索