本程序改編自《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()
踢開
通知
更新
值得注意的是:update()方法傳遞的參數,可以傳遞整個subject對象,也可以只傳遞部分有用的數據成員。實驗樓網站課程中的例子就是傳遞整個subject對象,《Head First Design Pattern》就是傳遞部分有用的數據成員。無論無何,最關鍵的是:
Subject對象中的數據成員發生改變
萬變離不開這個鏈條。
Java中有一個java.util.Observable類(被觀察者,Subject);還有一個java.util.Observer接口(觀察者,Obserer)。看來觀察者模式很常用,以至於都寫進到java類庫裏面去了。