Java設計模式(十)
------------觀察者模式
引入
我們這裏利用《Head.First.設計模式》中的那個氣象站的經典的例子坐引。
我們需要建立一個該氣象站,它必須建立在我們專利申請中的WeatherD ata對象上,由WeatherData對象負責追蹤目前的天氣狀況(溫度、溼度、氣壓)。我們還要建立一個應用,有三種佈告板,分別顯示目前的狀況、氣象統計及簡單的預報。當WeatherObject對象獲得最新的測量數據時,三種佈告板必須實時更新。
而且,我們還希望它是一個可以擴展的氣象站,也就是允許其他開發人員可以寫出自己的氣象佈告板,並插入此應用中。
好了,下面的例子我們都將圍繞這個例子進行。
定義與角色
定義
觀察者模式定義了對象之間的一對多依賴,這樣一來,當一個對象改變狀態時,它的所有依賴者都會收到通知並自動更新。
角色
抽象主題角色
public interface Subject {
public void addObserver(Observer o);
public void removeObserver(Observer o);
public void notifyObserver();
} |
具體主題角色
public class WeatherData implements Subject { List<Observer> listeners = new ArrayList<Observer>();
public int getTemperature() { return 0; }
public double getHumidity() { return 0.53; }
int getPressure() { return 30; } // 對外暴露接口 public void measurementsChanged() { this.notifyObserver(); } @Override public void addObserver(Observer o) { // TODO Auto-generated method stub listeners.add(o); } @Override public void notifyObserver() { // TODO Auto-generated method stub int t = this.getTemperature(); double h = this.getHumidity(); int p = this.getPressure(); for (int i = 0; i < listeners.size(); i++) { Observer listener = listeners.get(i); listener.onWebtherChange(t, h, p); } } @Override public void removeObserver(Observer o) { // TODO Auto-generated method stub listeners.remove(o); } } |
抽象觀察者角色
public interface Observer {
public void onWebtherChange(int t, double h, int p);
} |
具體觀察者角色
public class HWeatherPanel implements Observer {
public WeatherData data;
public HWeatherPanel(WeatherData data) { this.data = data; data.addObserver(this); }
@Override public void onWebtherChange(int x, double h, int p) { // TODO Auto-generated method stub System.out.println("更新溼度面板,最溼度是 " + h); }
} public class PWeatherPanel implements Observer { public WeatherData data;
public PWeatherPanel(WeatherData data) { this.data = data; data.addObserver(this); }
@Override public void onWebtherChange(int x, double h, int p) { // TODO Auto-generated method stub System.out.println("更新壓力面板,最新壓力是 " + p); }
} public class TWeatherPanel implements Observer { public WeatherData data;
public TWeatherPanel(WeatherData data) { this.data = data; data.addObserver(this); }
@Override public void onWebtherChange(int x, double h, int p) { // TODO Auto-generated method stub System.out.println("更新溫度面板,最新溫度是 " + x); }
} |
客戶調用
public class MyTest {
/** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub WeatherData data = new WeatherData(); HWeatherPanel hp = new HWeatherPanel(data); PWeatherPanel pp = new PWeatherPanel(data); TWeatherPanel tp = new TWeatherPanel(data);
data.measurementsChanged(); }
} 打印結果: 更新溼度面板,最溼度是 0.53 更新壓力面板,最新壓力是 30 更新溫度面板,最新溫度是 0 |
Java中內置的觀察者模式
到目前爲止,我們已經從無到有地完成了觀察者模式,但是,JavaAAPI有內置的觀察者模式。java.util包(package)內包含最基本的Observer接口與Observable類,這和我們的Subject接口與Observer接口很相似。Observer接口與Observable類使用上更方便,因爲許多功能都已經事先準備好了。
使用
接口 Observer
一個可在觀察者要得到 observable 對象更改通知時可實現
Observer
接口的類。
update(
Observable o,
Object arg)
很顯然 這個
Observer接口就是對應我們自己實現中的Observer接口,update(
Observable o,
Object arg)
方法就對應我們的
onWebtherChange(int t, double h, int p)方法,
這與我們的想法甚至名稱都是一致的。
類 Observable
|
由上表可以看出Observable類對應我們自己實現中的Subject接口,由於它是一個類,對於想要擴展的朋友就顯得無能爲力了,但是對於一般的觀察者應用已經足夠使了。
區別
我們不難發現,這個函數表中多個一個change的概念(setChanged(),hasChanged(),clearChanged())它的引入是爲了在更新觀察者時,有更多的彈性,你可以更適當地通知觀察者。比方說,如果沒有setChanged()方法,我們的氣象站測量是如此敏銳,以致於溫度計讀數每十分之一度就會更新,這會造成WeatherData對象持續不斷地通知觀察者,我們並不希望看到這樣的事情發生。如果我們希望半度以上才更新,就可以在溫度差距到達半度時,調用setChanged(),進行有效的更新。你也許不會經常用到此功能,但是把這樣的功能準備好,當需要時馬上就可以使用。總之,你需要調用setChanged(),以便通知開始運轉。如果此功能在某些地方對你有幫助,你可能也需要clearChanged()方法,將changed狀態設置回false。另外也有一個hasChanged()方法,告訴你changed標誌的當前狀態。
總結
優勢
主題對象與觀察者對象之間的松耦合,主題對象只知道Obserser接口,並不關誰實現了該接口以及怎麼實現的,一旦有新類型觀察者出現,我們只要把它註冊到主題中就可以了,不用修改任何代碼。
主要程序設計應用
這種設計模式廣泛用於數據或狀態的變化引起的界面刷新工作。
比如一個股票數據(價格)發生變化,可能會影響幾個視圖的更新(實時圖,表圖,甚至要入庫操作。。。),我們就需要把這幾個視圖和操縱數據庫的類作爲觀察者,註冊到相應主題中去。