客戶需求
/**
* 需求:
*
* 氣象站提供最新的天氣預報信息,包括: 溫度、濕度、氣壓,這些信息一發生變化, 客戶端的信息需要同時更新 請用代碼實現模擬實現該功能
*
*
*/
程序設計
一個氣象站對應著多個客戶端,氣象站的數據一發生變化,客戶端的數據也要隨著更新,這就形成了一種依賴關系,并且是一對多的關系,很顯然,我們可以用觀察者模式來完成此功能
基本概念
- 定義
定義了對象之間的一對多依賴,這樣一來,當一個對象改變狀態時,它的所有依賴者都會收到通知并自動更新
-
角色
- 抽象被觀察者(或者叫主題)
它是一個接口或者抽象類,用來把所有觀察者對象的引用保存到一個集合中。定義三個方法:注冊觀察者、解除觀察者、通知觀察者;這三個方法就是被觀察者與觀察者通信的橋梁
- 抽象觀察者
為所有的具體的觀察者定義的一個接口,定義一個方法:更新;用于獲取被觀察者發生變化時更新自己
- 具體的被觀察者(或者叫主題)
在被觀察者內部狀態發生變化,通知所有注冊過的的觀察者。通常實現或繼承抽象被觀察者
- 具體的觀察者
實現抽象觀察者接口,使自己與具體的被觀察者的狀態保持一致
UML圖
-
DemoOne
-
被觀察者
public interface Subject { public void registerObserver(Observer observer); public void unregisterObserver(Observer observer); public void notifyObservers(); }
-
觀察者
public interface Observer { public void update(float temp, float humidity, float pressure); }
-
具體被觀察者
public class WeatherDataSubject implements Subject { private float temperature; private float humidity; private float pressure; /** * 記錄所有的觀察者 */ private ArrayList<Observer> observers; public WeatherDataSubject() { observers = new ArrayList<>(); } @Override public void registerObserver(Observer observer) { observers.add(observer); } @Override public void unregisterObserver(Observer observer) { int indexOf = observers.indexOf(observer); if (indexOf >= 0) { observers.remove(indexOf); } } @Override public void notifyObservers() { for (int i = 0; i < observers.size(); i++) { Observer observer = observers.get(i); observer.update(temperature, humidity, pressure); } } public void setMeasurements(float temperature, float humidity, float pressure) { this.temperature = temperature; this.humidity = humidity; this.pressure = pressure; notifyObservers(); } }
-
具體觀察者
public class CurrentConditionsDidsplay implements Observer { @Override public void update(float temp, float humidity, float pressure) { System.out.println("temperature=" + temp + "\t humidity=" + humidity + "\t pressure=" + pressure); } }
-
測試
public class ObserverPatternTest { public static void main(String[] args) { WeatherDataSubject subject = new WeatherDataSubject(); CurrentConditionsDidsplay condition = new CurrentConditionsDidsplay(); subject.registerObserver(condition); subject.setMeasurements(23.0f, 1.0f, 350.0f); } }
-
以上這種方式只是簡單的實現了被觀察者數據發生變化時,主動將數據送過來,是一種推的模式。它不會管觀察者到底需不需要全部的數據。但是,有的觀察者可能只需要一點點數據,不想收到一堆數據,那此時怎么辦呢?那被觀察者是否可以提供一個get方法讓觀察者自己去獲取數據,僅通知數據有變化了?接下來我們看看Java內置的觀察者模式
- UML圖
- DemoTwo
-
具體的被觀察者
public class WeatherDataObservable extends Observable { private float temperature; private float humidity; private float pressure; public void setMeasurements(float temperature, float humidity, float pressure) { this.temperature = temperature; this.humidity = humidity; this.pressure = pressure; //僅僅是用來通知觀察者我的狀態發生變化了 setChanged(); notifyObservers(); } public float getTemperature() { return temperature; } public float getHumidity() { return humidity; } public float getPressure() { return pressure; } }
-
具體的觀察者
public class CurrentConditionsDidsplayObserver implements Observer { @Override public void update(Observable observable, Object obj) { if (observable instanceof WeatherDataObservable) { WeatherDataObservable data = (WeatherDataObservable) observable; float temperature = data.getTemperature(); float humidity = data.getHumidity(); float pressure = data.getPressure(); System.out.println("temperature=" + temperature + "\t humidity=" + humidity + "\t pressure=" + pressure); } } }
-
java內置的觀察者模式是屬于拉數據模式,被觀察者狀態發生變化了,通過setChanged方法僅通知觀察者狀態發生了變化,拉不拉數據是觀察者的事了,與被觀察者無關了。但是這種設計方式也會有一定的限制:
(1)Observable是一個類,所以我們的子類需繼承它,若此時被觀察者需同時繼承另一個超類,就會陷入兩難了,畢竟Java不支持多重繼承,這限制了Observable的復用能力
(2)Observable中的setChanged方法被protected了,這意味著:除非你繼承Observable類,否則你無法創建Observable實例并組合到你自己的對象中來。“多用組合,少用繼承”
知識總結
這兩種模式的使用,取決于系統設計時的需要。如果觀察者比較復雜,并且觀察者進行更新時必須得到一些具體變化的信息,則“推模式”比較合適。如果觀察者比較簡單,則“拉模式”就比較適合。