此次的引子是關於氣象站的應用,案例中要創建一個應用,有三種天氣預報的展示形式,使用一個WeatherObject對象得到最新測量到的天氣數據,而後對三個佈告板進行實時更新。而且之後可能會新加入其餘的佈告板,須要系統有很高的擴展性。WeatherData對象知道如何跟物理氣象站聯繫,以取得更新的數據。WeatherData對象會隨即更新三個佈告板的顯示:目前情況(溫度、溼度、氣壓)、氣象統計和天氣預報。git
這是WeatherData類github
WeatherData
getTemperature( )
getHumidity( )
getPressure( )
measure- mentsChanged( )
/*
* 一旦氣象測量更新,此方法會被調用
*/
public void measurementsChanged() {
// 你的代碼加在這裏
}
複製代碼
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
這段程序就是針對具體實現編程,而不是針對接口編程,咱們須要將類似的地方封裝起來。編程
咱們看看報紙和雜誌的訂閱是怎麼回事:設計模式
報社的業務就是出版報紙。bash
向某家報社訂閱報紙,只要他們有新報紙出版,就會給你送來。ui
只要你是他們的訂戶,你就會一直收到新報紙。this
當你不想再看報紙的時候,取消訂閱,他們就不會再送新報紙來。spa
只要報社還在運營,就會一直有人(或單位)向他們訂閱報紙或取消訂閱報紙。設計
定義觀察者模式
觀察者模式:定義了對象之間的一對多依賴,這樣一來,當一個對象改變狀態時,它的全部依賴者都會收到通知並自動更新。
觀察者模式定義了一系列對象之間的一對多關係。
當一個對象改變狀態,其餘依賴者都會收到通知。
當兩個對象之間鬆耦合,它們依然能夠交互,可是不太清楚彼此的細節。 觀察者模式提供了一種對象設計,讓主題和觀察者之間鬆耦合。
關於觀察者的一切,主題只知道觀察者實現了某個接口(也就是Observer接口)。主題不須要知道觀察者的具體類是誰、作了些什麼或其餘任何細節。 任 何 時 候 我 們 都 可 以 增 加 新 的 觀 察 者 。 因 爲 主 題 惟 一 依 賴 的 東 西 是 一 個 實 現Observer接口的對象列表,因此咱們能夠隨時增長觀察者。事實上,在運行時咱們能夠用新的觀察者取代現有的觀察者,主題不會受到任何影響。一樣的,也能夠在任什麼時候候刪除某些觀察者。
改變主題或觀察者其中一方,並不會影響另外一方。由於二者是鬆耦合的,因此只要他們之間的接口仍被遵照,咱們就能夠自由地改變他們。
設計原則:爲了交互對象之間的鬆耦合設計而努力
依照前面的類圖,開始編寫氣象站的代碼
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能夠獲得結果,由此整個代碼編寫完成。