設計模式——觀察者模式 Java源代碼

本程序改編自《Head First Design Patterns》上關於氣象站的例子,我將例子進行了化簡。

總共7個java源文件

一個Subject接口
一個Observer接口
一個DisplayElement接口
一個Subject具體類,實現Subject接口
兩個Observer具體類,實現Observer和DisplayElement接口
一個Main類,用於測試

觀察者模式類圖
這裏寫圖片描述
圖:我用插件畫的類圖

The Observation Pattern defines a one-to-many dependency。這裏的one就是Subject, many就是Observers;one就是發佈者,many就是訂閱者;one就是事件源,many就是監聽者。


Talk is cheap, show me the code

一個Subject接口

package observer;

public interface Subject
{
    public void registerObserver(Observer o);
    public void removeOvserver(Observer o);
    public void notifyObservers();
}

一個Observer接口

package observer;

public interface Observer
{
    public void update(double temperature, double humidity, String condition);
}

一個DisplayElement接口

這個接口只有display( )一個抽象方法,其實如果放在Observer裏面的話,僅從代碼層面考慮的話,也可以。但是如果從邏輯層面考慮的話,最好還是分開。畢竟這個display和觀察者是”兩碼事“,不要耦合在一起。《Head First Design Pattern》上面的例子代碼這樣寫,是爲了描繪出一個生動形象的”氣象站“的例子,來幫助大家理解觀察者模式。

package observer;

public interface DisplayElement
{
    public void display();
}

一個Subject具體類

package observer;

import java.util.ArrayList;
             // Subject收集天氣信息,然後通知Observers
public class WeatherData implements Subject
{
    private ArrayList<Observer> observers;
    private double temperature;
    private double humidity;
    private String condition;

    public WeatherData()
    {
        observers = new ArrayList<Observer>();
    }

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

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

    @Override
    public void notifyObservers()
    {
        for(Observer observer : observers)
        {
            observer.update(temperature, humidity, condition);
        }
    }

    public void measurementsChanged()
    {
        notifyObservers();
    }

    public void setMeasurements(double temperature, double humidity, String condition)
    {
        this.temperature = temperature;
        this.humidity = humidity;
        this.condition = condition;
        measurementsChanged();
    }

    public double getTemperature()
    {
        return temperature;
    }

    public double getHumidity()
    {
        return humidity;
    }

    public String getPressure()
    {
        return condition;
    }

}

兩個Observer具體類

package observer;
             // 顯示當前天氣情況
public class CurrentDisplay implements Observer, DisplayElement
{
    private double temperature;
    private double humidity;
    private String condition;

    public CurrentDisplay(Subject weatherData)
    {
        weatherData.registerObserver(this);
    }

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

    @Override
    public void update(double temperature, double humidity, String condition)
    {
        this.temperature = temperature;
        this.humidity = humidity;
        this.condition = condition;
        display();
    }

    public String toString()
    {
        return "This is Current Condition Display";
    }
}
package observer;
             // 顯示歷史統計數據
public class StatisticDisplay implements Observer, DisplayElement
{
    private double maxTemperature = 0;
    private double minTemperature = 100;
    private double temperatureSum = 0;
    private int    totalUpdating = 0;

    public StatisticDisplay(Subject weatherData)
    {
        weatherData.registerObserver(this);
    }

    @Override
    public void display()
    {
        System.out.println("Avg/Max/Min temperture: " + temperatureSum/totalUpdating + "/" 
                + maxTemperature + "/" + minTemperature);
    }

    @Override
    public void update(double temperature, double humidity, String condition)
    {
        temperatureSum += temperature;
        totalUpdating++;
        if(temperature > maxTemperature)
        {
            maxTemperature = temperature;
        }
        if(temperature < minTemperature)
        {
            minTemperature = temperature;
        }
        display();
    }

    public String toString()
    {
        return "This is Statictic Display";
    }
}

一個Main測試類

package observer;

public class Main
{

    public static void main(String[] args)
    {
        WeatherData weatherStation = new WeatherData(); // 一個氣象站作爲Subject

        CurrentDisplay currentDisplay = new CurrentDisplay(weatherStation);       // 註冊Observer1
        System.out.println(currentDisplay);    // 這個是爲了不讓eclipse報出warning                       
        StatisticDisplay statisticDisplay = new StatisticDisplay(weatherStation); // 註冊Observer2
        System.out.println(statisticDisplay);  // 這個是爲了不讓eclipse報出warning                                   


        weatherStation.setMeasurements(20, 30, "晴天");  // 氣象站測量出了新的天氣,在第一時間通知所有的觀察者
        weatherStation.setMeasurements(16, 50, "多雲");
        weatherStation.setMeasurements(12, 60, "大風");
        weatherStation.setMeasurements(12, 90, "小雨");
        weatherStation.setMeasurements(11, 98, "大雨");
    }

}

運行結果

直接從eclipse複製過來

This is Current Condition Display
This is Statictic Display
Current Conditions: 20.0 C degrees and 30.0% humidity 晴天
Avg/Max/Min temperture: 20.0/20.0/20.0
Current Conditions: 16.0 C degrees and 50.0% humidity 多雲
Avg/Max/Min temperture: 18.0/20.0/16.0
Current Conditions: 12.0 C degrees and 60.0% humidity 大風
Avg/Max/Min temperture: 16.0/20.0/12.0
Current Conditions: 12.0 C degrees and 90.0% humidity 小雨
Avg/Max/Min temperture: 15.0/20.0/12.0
Current Conditions: 11.0 C degrees and 98.0% humidity 大雨
Avg/Max/Min temperture: 14.2/20.0/11.0


總結

這個觀察者模式,多個觀察者(Observer)“圍觀”一個被觀察者(Subject),被觀察者是萬衆矚目的焦點。觀察者如果想得到Subject的“觀察權”,首先需要向Subject 申請註冊,如果惹Subject生氣了,不讓你觀察了,就把你踢開。Subject一旦發生了變化,就立刻通知所有觀察者,引起觀察者的更新

黑體字就是Subject接口中的三個和Observer接口中的一個關鍵的抽象方法,必須實現:
申請註冊:registerObserver()
踢開       :removeObserver()
通知       :notifyObservers()
更新       :update()

值得注意的是:update()方法傳遞的參數,可以傳遞整個subject對象,也可以只傳遞部分有用的數據成員。實驗樓網站課程中的例子就是傳遞整個subject對象,《Head First Design Pattern》就是傳遞部分有用的數據成員。無論無何,最關鍵的是:
Subject對象中的數據成員發生改變
觸發Subject.notifyObservers()方法
觸發所有Observer.update() 方法。
萬變離不開這個鏈條。

Java中有一個java.util.Observable類(被觀察者,Subject);還有一個java.util.Observer接口(觀察者,Obserer)。看來觀察者模式很常用,以至於都寫進到java類庫裏面去了。

更多設計模式,在新標籤頁中打開這裏

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