設計模式學習(三) 觀察者模式
引入
定義:定義了物件之間的一對多依賴,這樣一來,當一個物件改變狀態時,他的所有依賴都會收到通知並自動更新。
認識觀察者模式
例子:報紙和雜誌的訂閱
1.報社的業務就是出版報紙
2.向某家報社訂閱報紙,只要有新的報紙出版,就會一直為你送報紙
3.當你取消了訂閱後,將不會為你送新報紙
4.只要報社在運營,就會一直有人或單位訂閱報紙或取消訂閱。
出版社+訂閱者=觀察者 模式
UML類圖
以上包含subject與observer介面的類設計最為常見
1.subject主題介面,物件使用此介面註冊為觀察者或者把自己從觀察者中刪除
2.observer所有的觀察者必須實現此介面,這個介面只有一個update方法。當主題狀態改變時它被呼叫
3.ConcreteSubject一個具體主題實現了主題介面,除了註冊和撤銷方法,還實現了notifyObserver方法,此方法在主題 狀態改變時更新所有當前觀察者
4.具體的觀察者可以是實現Observer介面的任意類,觀察者必須註冊主題,以便接受更新
示例
這裡引用head first上一個例子
package com.zpkj.project4; /** * subject介面 */ public interface SubjectInter { void registerObserver(ObserverInter observerInter); void removeObserver(ObserverInter observerInter); void notifyObserver(); }
package com.zpkj.project4;
/** * Observer介面 */
public interface ObserverInter { void update(float temp,float humidity,float pressure);
}
package com.zpkj.project4;
import java.util.ArrayList;
/** * ConcreteSubject 實現類 */
public class WeatherData implements SubjectInter{ private ArrayList<ObserverInter> observerInters; private float temperature; private float humidity; private float pressure; public WeatherData() { super(); observerInters =new ArrayList<ObserverInter>(); }
@Override public void registerObserver(ObserverInter observerInter) { observerInters.add(observerInter); }
@Override public void removeObserver(ObserverInter observerInter) { int num = observerInters.indexOf(observerInter); if(num>=0){ observerInters.remove(num); } }
@Override public void notifyObserver() { for(int i = 0;i < observerInters.size();i++){ ObserverInter observerInter = observerInters.get(i); observerInter.update(temperature, humidity, pressure); } } public void measurementsChanged(){ notifyObserver(); } public void setMeasurements(float temperature,float humidity,float pressure){ this.temperature = temperature; this.humidity = humidity; this.pressure = pressure; measurementsChanged(); }
}
package com.zpkj.project4;
/** * ConcreteObserver 實現類 */
public class CurrentConditionsDisplay implements ObserverInter,DisplayElementInter{ private SubjectInter weatherData; private float temperature; private float humidity; private float pressure; public CurrentConditionsDisplay(SubjectInter subjectInter){ super(); this.weatherData = subjectInter; weatherData.registerObserver(this); }
@Override public void display() { System.out.println("氣溫:"+temperature+" 溼度:"+humidity+" 壓力:"+pressure); }
@Override public void update(float temp, float humidity, float pressure) { this.temperature = temp; this.humidity = humidity; this.pressure = pressure; display(); }
}
/** * 負責顯示天氣資訊介面 */
package com.zpkj.project4;
public interface DisplayElementInter { void display();
}
有幾點需要注意的是
- Subject 和 Observer 是一個一對多的關係,也就是說觀察者只要實現 Observer 介面並把自己註冊到 Subject 中就能夠接收到訊息事件;
- Java API有內建的觀察者模式類:java.util.Observable 類和 java.util.Observer 介面,這分別對應著 Subject 和 Observer 的角色;
- 使用 Java API 的觀察者模式類,需要注意的是被觀察者在呼叫 notifyObservers() 函式通知觀察者之前一定要呼叫 setChanged() 函式,要不然觀察者無法接到通知;
- 使用 Java API 的缺點也很明顯,由於 Observable 是一個類,java 只允許單繼承的缺點就導致你如果同時想要獲取另一個父類的屬性時,你只能選擇介面卡模式或者是內部類的方式,而且由於 setChanged() 函式為 protected 屬性,所以你除非繼承 Observable 類,否則你根本無法使用該類的屬性,這也違背了設計模式的原則:多用組合,少用繼承。