上一篇的觀察者模式學習的還好嗎?首先簡單來回顧下上篇內容,有一個氣象站的需求,需要在溫度、濕度、氣壓改變的時候,實時更新三個佈告板,以便能及時、準確的獲取信息。所以,在設計模式的層面,我們最容易想到並且最正確的方式就是使用觀察者模式來處理這個問題。 上一篇,我們通過一系列的分析,並畫出符合要求的類圖 ...
上一篇的觀察者模式學習的還好嗎?首先簡單來回顧下上篇內容,有一個氣象站的需求,需要在溫度、濕度、氣壓改變的時候,實時更新三個佈告板,以便能及時、準確的獲取信息。所以,在設計模式的層面,我們最容易想到並且最正確的方式就是使用觀察者模式來處理這個問題。
上一篇,我們通過一系列的分析,並畫出符合要求的類圖,然後使用了第一種方式,通過自己動手寫設計模式實現了觀察者模式的功能。但是,截止到目前為止,我們觀察者模式的方式都是由被觀察者主動發送狀態通知給觀察者,從而達到實時更新。這樣有個弊端就是被觀察者不能事先知道觀察者每個人的需求,有些觀察者其實只需要一點點數據,但無奈會受到一堆數據。有沒有方式,讓觀察者通過getter方法,主動獲取新增的狀態呢。答案是,有。Java內置的Observer模式兩種方法都支持。
java.util包內包含基本的Observer介面與Observable類,這和我們的Subject介面與Observer介面很相似。Observer介面與Observable類使用上更方便。因為許多功能都事先準備好了。你甚至可以使用推(PUSH)或拉(PULL)的方式傳送數據。下麵就看看,我們修改後的氣象站OO設計。
Java內置的觀察者模式如何運作
Java內置的觀察者模式運作方式,和我們在氣象站中的實現類似,但有一點小差異。最明顯的差異是WeatherData現在擴展自Obserable類,並繼承到一些增加、刪除、通知觀察者的方法(以及其他的方法)。Java版本的用法如下:
如何把對象變成觀察者
如同以前一樣,實現觀察者介面(java.util.Observer),然後調用任何Observable對象的 addObserver()方法。不想再當觀察者時,調用deleteObserver()方法即可。
可觀察者要如何送出通知
首先,你需要利用擴展java.util.Observable介面產生"可觀察者"類 ,然後,需要兩個步驟:
- 先調用setChanged()方法,標記狀態已經改變的事實
- 然後調用兩種notifyObservers()方法中的一個:notifyObservers()或notifyObservers(Object arg)
觀察者如何接收通知
同以前一樣,觀察者實現了更新的方法,但是方法的簽名不太一樣:
update(Observable o, Object arg)
o:主題本身當做第一個變數,好讓觀察者知道是哪個主題通知它的
arg:這正是傳入notifyObservers()的數據對象。如果沒有說明,則為空。
如果你想推(push)數據給觀察者,你可以把數據當做數據對象傳給notifyObservers(arg0)方法。否則,觀察者就必須從可觀察者對象中拉(pull)數據,如何拉數據,不急不急,我們很快和大家見面。
利用內置的支持重做氣象站
首先,把WeatherData改成使用java.util.Observable
package com.dimple.headfirst.observer;
// 1:記得要導入(import)正確的Observer/Observable
import java.util.Observable;
/**
*
* @Title:
* @Description: 2:我們現在正繼承Observable
* 3:我們不需要追蹤觀察者了,也不需要管理註冊與刪除(讓超類代勞即可)。
* 我們把註冊、添加、通知的相關代碼刪除
*/
public class WeatherDataNew extends Observable {
private float temperature;
private float humidity;
private float pressure;
// 4:我們的構造器不再需要為了記住觀察者們而建立數據結構了
public WeatherDataNew() {
}
public void measurementsChanged() {
// 5: 調用notifyObservers()之前,要先調用setChanged()來指示狀態已經改變
setChanged();
// 註意:我們沒有調用notifyObservers()傳送數據對象,這表示我們採用的做法是拉
notifyObservers();
}
public void setMeasurements(float temperature, float humidity,float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
measurementsChanged();
}
// 6:這些並不是新方法,只是因為我們要使用“拉”的做法,所以才提醒你有這些方法。觀察者會利用這些方法取得WeatherData對象的狀態
public float getTemperature() {
return temperature;
}
public float getHumidity() {
return humidity;
}
public float getPressure() {
return pressure;
}
}
現在,讓我們重做CurrentConditionsDisplay
package com.dimple.headfirst.observer;
import java.util.Observable;
// 1:導入正確的Observer/Observable
import java.util.Observer;
/**
*
* @Title:
* @Description: 2:我們正在實現java.util.Observer介面
*/
public class CurrentConditionDisplayNew implements Observer, DisplayElement {
Observable observable;
private float temperature;
private float humidity;
// 3:構造器需要 Observable當參數,並將 CurrentConditionsDisplay對象登記成觀察者
public CurrentConditionDisplayNew(Observable observable) {
this.observable = observable;
observable.addObserver(this);
}
// 4:改變update方法,增加Observable和數據對象作為參數
@Override
public void update(Observable obs, Object arg) {
// 5:在update()中,先確定可觀察者屬於WeatherData類型,然後利用getter方法獲取溫度和濕度測量值,最後調用display()
if (obs instanceof WeatherDataNew) {
WeatherDataNew weatherDataNew = (WeatherDataNew)obs;
this.temperature = weatherDataNew.getTemperature();
this.humidity = weatherDataNew.getHumidity();
display();
}
}
// 顯示溫度和濕度
@Override
public void display() {
System.out.println("Current conditions: " + temperature + " F degrees and " + humidity + "% humidity");
}
}
運行新的代碼
public class WeatherStation {
public static void main(String[] args) {
WeatherDataNew weatherDataNew = new WeatherDataNew();
CurrentConditionDisplayNew currentConditionDisplayNew = new CurrentConditionDisplayNew(weatherDataNew);
weatherDataNew.setMeasurements(80, 65, 30.4f);
}
}
// 結果顯示
Current conditions: 80.0 F degrees and 65.0% humidity
至此,我們就把Java內置的觀察者模式學習了一遍,你學會了嗎?
但是,不知道你發現沒有,內置的觀察者模式中,可觀察者是一個“類”而不是一個“介面”,甚至沒有實現一個介面,這樣就限制了他的使用和復用,所以還是需要做下強調,提醒大家這是一個“類”的事實。
Observable是一個類
首先,因為Observable是一個“類”,你必須設計一個類繼承它。如果某類想同事具有Observable類和另一個超類的行為,就會有糾結,畢竟Java只支持單繼承嘛。
Observable將關鍵的方法保護起來
protected synchronized void setChanged() {
changed = true;
}
setChanged()方法是一個保護方法,這就意味著除非你繼承自Observable,否則你無法創建Observable實例並組合到你自己的對象中來。這個設計也就違反了之前第二個設計原則“多用組合,少用繼承”。
做什麼呢?
如果你能夠擴展java.util.Obserable,那麼Observable“可能”可以符合你的需求。否則,你可能需要像我們上一篇那樣,自己去實現一套觀察者模式,不過不管怎樣,相信你都已經掌握了觀察者模式,這已經難不倒你了。
因為觀察者模式是平時使用比較廣泛,並且大家會頻繁的接觸甚至是使用,所以我本來想在這篇進行總結的,考慮到篇幅的原因,讓這個總結放到下一篇,通過這兩個篇幅的學習和實踐,把自己動手寫觀察者模式以及使用Java內置的方式來使用觀察者模式都進行了掌握。其他語言的不知道有沒有內置的,只能說Java程式員在看完這篇文章之後,肯定又多了一種選擇,給自己鼓掌吧。
PS:代碼已經上傳,需要查看的朋友點擊此處HeadFirstDesign
愛生活,愛學習,愛感悟,愛挨踢