一、是什么?作用
1. 觀察者模式定義了一系列對象之間的一對多關系
2. 當一個對象改變狀態, 其他依賴着都會受到通知
二、示例
需求: 氣象觀測天氣,並定時更新觀測數據,我們的電子儀器需要接受這些數據
1. PUSH模式, 由被觀察者推送給觀察者
1. 定義主題, 即被觀察者
/** * 主題 */ public interface Subject { // 添加觀察者 void registerObserver(Observer o); // 刪除觀察者 void removeObserver(Observer o); // 通知 void notifyObservers(); }
2. 定義觀察者的超類,里面只有一個更新的方法
/** * 觀察者超類 */ public interface Observer { public void update(float temp, float humidity, float pressure); }
3. 天氣數據, 主題的子類
1. 有一個屬性來存放觀察者
2. 實現主題的方法
3. 有三個數據需要傳遞(溫度、濕度、氣壓)
1 /** 2 * 天氣數據, 實現主題 3 */ 4 public class WeatherData implements Subject { 5 6 /** 7 * 存儲觀察者 8 */ 9 private List<Observer> observers; 10 private float temperature; 11 private float humidity; 12 private float pressure; 13 14 public WeatherData() { 15 this.observers = new ArrayList<>(); 16 } 17 18 /** 19 * 當氣象站得到更新觀測值時,我們通知觀察者 20 */ 21 public void measurementsChanged() { 22 notifyObservers(); 23 } 24 25 /** 26 * 模擬氣象站更改了氣象數據 27 */ 28 public void setMeasurements(float temperature, float humidity, float pressure) { 29 // 更改氣象數據 30 this.temperature = temperature; 31 this.humidity = humidity; 32 this.pressure = pressure; 33 34 // 通知觀察者 35 measurementsChanged(); 36 } 37 38 @Override 39 public void registerObserver(Observer o) { 40 observers.add(o); 41 } 42 43 @Override 44 public void removeObserver(Observer o) { 45 int i = observers.indexOf(0); 46 if (i >= 0) { 47 observers.remove(0); 48 } 49 } 50 51 @Override 52 public void notifyObservers() { 53 for (Observer observer : observers) { 54 observer.update(temperature, humidity, pressure); 55 } 56 } 57 58 public float getTemperature() { 59 return temperature; 60 } 61 62 public float getHumidity() { 63 return humidity; 64 } 65 66 public float getPressure() { 67 return pressure; 68 } 69 }
4. 觀察者具體類, 實現觀察者,將主題通過構造傳入,並訂閱(這里將耦合度降到了很低了),實現更新方法
/** * 顯示當前天氣的儀器 - 觀察者的具體類 */ public class NowConditionDisplay implements Observer { private float temperature; private float humidity; private WeatherData weatherData; public NowConditionDisplay(WeatherData weatherData) { this.weatherData = weatherData; weatherData.registerObserver(this); } @Override public void update(float temp, float humidity, float pressure) { this.temperature = temp; this.humidity = humidity; display(); } public void display() { System.out.println("[push] 當前溫度:" + temperature + " 當前濕度: " + humidity); } }
測試
public class pushMain { public static void main(String[] args) { WeatherData weatherData = new WeatherData(); NowConditionDisplay nowConditionDisplay = new NowConditionDisplay(weatherData); weatherData.setMeasurements(20, 10, 29.2f); } } 控制台顯示: [push] 當前溫度:20.0 當前濕度: 10.0
2. PULL模式, 由觀察者去拉數據
1. 天氣數據提供者,利用java.util包下的Observable類來實現
import java.util.Observable; /** * 數據提供者 */ public class WeatherData extends Observable { // 三個數據 private float temperature; private float humidity; private float pressure; public WeatherData() { } public void measurementChanged() { setChanged(); notifyObservers(); } public void setMeasurements(float temperature, float humidity, float pressure) { this.temperature = temperature; this.humidity = humidity; this.pressure = pressure; measurementChanged(); } public float getTemperature() { return temperature; } public float getHumidity() { return humidity; } public float getPressure() { return pressure; } }
2. 觀察者子類, 實現java.util的Observer
import java.util.Observable; import java.util.Observer; /** * @author wb-xp570304 on 2019/7/9 */ public class NowConditionDisplay implements Observer { Observable observable; private float temperature; private float humidity; public NowConditionDisplay(Observable observable) { this.observable = observable; observable.addObserver(this); } @Override public void update(Observable o, Object arg) { if (o instanceof WeatherData) { WeatherData weatherData = (WeatherData) o; this.temperature = weatherData.getTemperature(); this.humidity = weatherData.getHumidity(); display(); } } public void display() { System.out.println("當前溫度: " + temperature + " 濕度: " + humidity); } }
測試:
public class pullMain { public static void main(String[] args) { WeatherData weatherData = new WeatherData(); NowConditionDisplay nowConditionDisplay = new NowConditionDisplay(weatherData); weatherData.setMeasurements(80, 65, 30f); } }
控制台顯示:
當前溫度: 80.0 濕度: 65.0
小結:
第二種pull的方式這里是利用java.util.Observable來實現的,而且用的是繼承,減少了代碼的可擴展性
三、總結
1. 觀察者模式的理念是,為交互對象之間的松耦合設計而努力