問題:
=======條件=======
一個氣象站:可以用來採集溫度、溼度、氣壓等信息
一個WheatherData類:可用來獲取氣象站的數據,並且提供了一些自己的方法
public class WeatherData {
private int temperature;
private int humidity;
private int pressure;
public int getHumidity() {
return humidity;
}
public int getPressure() {
return pressure;
}
public int getTemperature() {
return temperature;
}
//溫度、溼度、壓力改變時會調用這個
void parameterChanged(){
}
}
=======要求=======
有一個佈告欄,可以實時更新氣象臺的數據,並且我們可以肆意的刪減佈告欄的類型和數量
解決方案一:
====《1》創建統一的Display佈告欄接口:
public abstract class Display {
abstract void update(int tem,int hum,int pre);
abstract void display();
}
====《2》創建佈告欄子類NanjingDisplay:
public class NanjingDisplay extends Display{
private int temp;
private int hum;
private int pre;
@Override
void update(int tem, int hum, int pre) {
this.hum = hum;
this.pre = pre;
this.temp = tem;
display();
}
@Override
void display() {
System.out.println("溫度:"+temp+" 溼度: "+hum+" 氣壓: "+pre);
}
}
====《3》更新NanjingDisplay:
public class WeatherData {
private int temperature;
private int humidity;
private int pressure;
public int getHumidity() {
return humidity;
}
public int getPressure() {
return pressure;
}
public int getTemperature() {
return temperature;
}
//溫度、溼度、壓力改變時會調用這個
void parameterChanged(){
int temp = getTemperature();
int hum = getHumidity();
int pre = getPressure();
nanjingDisplay.update(temp,hum,pre);
}
}
這裏我們不得不考慮客戶的要求:多個不同種類的佈告欄
1.傳參:我們怎麼在WheatherData中獲得Display的引用
放在構造函數參數中傳入,Display數量龐大的話會很麻煩
2.WheatherData的封裝性考慮:
如果後續每多增加或者減少一個Diaplay類別的話,我們都得修改
parameterChanged()函數,這還只是一個onChanged函數,如果客戶後續
添加別的類別的數據的changed函數,可以想像到時後的代碼維護將會是非常恐
怖的工作量
方案一fail!
解決方案二:
1》changed()函數頻繁修改解決方案:
根據我們之前策略模式中提到的封裝變化規則,將changed()函數中的變數提取
成一個接口,很顯然這個接口叫Display,而我們要調用他們的Diaplay.update()方法
2》傳參數量解決方案:
這很簡單創建一個Dispaly[]數組即可,我們可以藉助數組進行增加和刪減操作
以上這些解決方案就接近我們的觀察者模式了
3》現在讓我們加入對接口編程這個規則,把對Display數組的增刪操作封裝成一個接口,
Observable,這個就是被監聽者了,將所有的希望接受氣象數據的Display抽象成Observer接口
4》在加入多用組合少用繼承的規則,在Observable中用Display[]數組將 Observer們組合到自己旗下;
所謂組合,即形容此時在Display[]數組中這些 Observer對象之間的關係,他們不是通過繼承來達到的,而是組合
下面引入觀察者模式框架圖
下面引入代碼
三個接口:
public interface Observable {
boolean registerObserver(Observer o);
boolean unregisterObserver(Observer o);
void notifyObservers();
}
public interface Observer {
void update(int arg0,int arg1,int arg2);
}
public abstract class Display {
abstract void display();
}
Display子類:
public class NanjingDisplay extends Display implements Observer{
private int temp;
private int hum;
private int pre;
private Observable observable;
public NanjingDisplay(Observable observable) {
//這裏認爲要不要監聽氣象數據應該是各個Display自己應該
//思考的事,故而將這個註冊行爲寫在NanjingDisplay類內
observable.registerObserver(this);
//這裏需要保留observable的引用,方便unregister
this.observable = observable;
}
@Override
public void update(int tem, int hum, int pre) {
this.hum = hum;
this.pre = pre;
this.temp = tem;
display();
}
@Override
void display() {
System.out.println("南京氣象更新=="+"溫度:"+temp+" 溼度: "+hum+" 氣壓: "+pre);
}
}
WheatherData類:
public class WeatherData implements Observable{
private int temperature;
private int humidity;
private int pressure;
public int getHumidity() {
return humidity;
}
public int getPressure() {
return pressure;
}
public int getTemperature() {
return temperature;
}
//溫度、溼度、壓力改變時會調用這個
void parameterChanged(int arg0,int arg1,int arg2){
temperature = arg0;
humidity = arg1;
pressure = arg2;
notifyObservers();
}
ArrayList<Observer> observers = new ArrayList<>();
@Override
public boolean registerObserver(Observer o) {
if (o ==null){
return false;
}
observers.add(o);
return true;
}
@Override
public boolean unregisterObserver(Observer o) {
int i = observers.indexOf(o);
if (i < 0){
return false;
}else{
observers.remove(i);
return true;
}
}
@Override
public void notifyObservers() {
if (observers.size() == 0){
return;
}
for(Observer o: observers){
o.update(temperature, humidity, pressure);
}
}
}
Test測試類:
public class Test {
public static void main(String[] args) {
WeatherData observable = new WeatherData();
Display nanjingDisplay = new NanjingDisplay(observable);
Display beijingDisplay = new BeijingDisplay(observable);
observable.parameterChanged(1,2,3);
observable.parameterChanged(4,5,6);
observable.parameterChanged(7,8,9);
}
}
測試結果:
南京氣象更新==溫度:1溼度:2氣壓:3
北京氣象更新==溫度:1溼度:2氣壓:3
南京氣象更新==溫度:4溼度:5氣壓:6
北京氣象更新==溫度:4溼度:5氣壓:6
南京氣象更新==溫度:7溼度:8氣壓:9
北京氣象更新==溫度:7溼度:8氣壓:9
注意:還要考慮一個問題,update的3個參數傳遞
1》作爲update的參數直接傳,最爲省事,但是存在問題,不是每個Display的子類都需要完整的
3個參數,可能是1個、2個、或者不需要,這樣就造成了”浪費“
2》從上面的例子我們可以看到在WeatherData中還”隱藏“了get方法,而且Display子類中也保存了WeatherData的對象,so,可以通過WeatherData.get來獲取自己需要的參數也 可以不獲取,但是這樣,會給Display的子類帶來負擔
此處示情況而定
下面給出觀察者模式的定義
在對象之間設置一對多的依賴,這樣一來,當對象的狀態改變的時候,依賴他的多個對象就可以收到通知,並自動更新
觀察者模式優點
將可觀察者(Observable)和觀察者解耦,WeatherData不用關心 NanjingDisplay具體是個幹什麼的,
準確的說WeatherData只知道自己接收了一個Observer類型的,而NanjingDiaplay也只是根據自己需要,
向一個 Observable註冊了