觀察者模式定義了對象之間的一對多依賴,這樣一來,當一個對象改變狀態時,它的全部依賴者都會收到通知並自動更新。ide
要點:函數
主題父類/接口主要提供了註冊觀察者、移除觀察者、通知觀察者三個方法。測試
/** * 主題 */ public class Subject { /** * 觀察者列表 */ private ArrayList<Observer> observers; public Subject() { observers = new ArrayList<>(); } /** * 註冊觀察者 */ public void registerObserver(Observer o) { observers.add(o); } /** * 移除觀察者 */ public void removeObserver(Observer o) { observers.remove(o); } /** * 通知全部觀察者,並推送數據(也能夠不推送數據,而是由觀察者過來拉取數據) */ public void notifyObservers(Object data) { for (Observer o : observers) { o.update(data); } } }
觀察者接口主要提供了更新方法,以供主題通知觀察者時調用。this
/** * 觀察者接口 */ public interface Observer { /** * 根據主題推送的數據進行更新操做 */ public void update(Object data); }
/** * 主題A */ public class SubjectA extends Subject { /** * 主題數據 */ private String data; public String getData() { return data; } public void setData(String data) { this.data = data; // 數據發生變化時,通知觀察者 notifyObservers(data); } }
經過觀察者類的構造函數,註冊成爲主題的觀察者。code
/** * 觀察者A */ public class ObserverImplA implements Observer { private Subject subject; public ObserverImplA(Subject subject) { // 保存主題引用,以便後續取消註冊 this.subject = subject; // 註冊觀察者 subject.registerObserver(this); } @Override public void update(Object data) { System.out.println("Observer A:" + data.toString()); } }
/** * 觀察者B */ public class ObserverImplB implements Observer { private Subject subject; public ObserverImplB(Subject subject) { // 保存主題引用,以便後續取消註冊 this.subject = subject; // 註冊觀察者 subject.registerObserver(this); } @Override public void update(Object data) { System.out.println("Observer B:" + data.toString()); } }
public class Test { public static void main(String[] args) { // 主題 SubjectA subject = new SubjectA(); // 觀察者A ObserverImplA observerA = new ObserverImplA(subject); // 觀察者B ObserverImplB observerB = new ObserverImplB(subject); // 模擬主題數據變化 subject.setData("I'm Batman!!!"); subject.setData("Why so serious..."); } }
你的團隊剛剛贏得一紙合約,負責創建 Weather-O-Rama 公司的下一代氣象站——Internet 氣象觀測站。server
該氣象站創建在 WeatherData 對象上,由 WeatherData 對象負責追蹤目前的天氣情況(溫度、溼度、氣壓)。而且具備三種佈告板,分別顯示目前的情況、氣象統計以及簡單的預報。當 WeatherData 對象得到最新的測量數據時,三種佈告板必須實時更新。對象
而且,這是一個可擴展的氣象站,Weather-O-Rama 氣象站但願公佈一組 API,好讓其餘開發人員能夠寫出本身的氣象佈告板,並插入此應用中。繼承
/** * 主題 */ public class Subject { /** * 觀察者列表 */ private ArrayList<Observer> observers; public Subject() { observers = new ArrayList<>(); } /** * 註冊觀察者 */ public void registerObserver(Observer o) { observers.add(o); } /** * 移除觀察者 */ public void removeObserver(Observer o) { observers.remove(o); } /** * 通知全部觀察者,並推送數據 */ public void notifyObservers(float temperature, float humidity, float pressure) { for (Observer o : observers) { o.update(temperature, humidity, pressure); } } }
/** * 觀察者接口 */ public interface Observer { /** * 更新觀測值 */ public void update(float temperature, float humidity, float pressure); }
/** * 氣象數據 */ public class WeatherData extends Subject { /** * 溫度 */ private float temperature; /** * 溼度 */ private float humidity; /** * 氣壓 */ private float pressure; public void measurementsChanged() { // 觀測值變化時,通知全部觀察者 notifyObservers(temperature, humidity, pressure); } /** * 設置觀測值(模擬觀測值變化) */ public void setMeasurements(float temperature, float humidity, float pressure) { this.temperature = temperature; this.humidity = humidity; this.pressure = pressure; measurementsChanged(); } }
/** * 目前狀態佈告板 */ public class CurrentConditionsDisplay implements Observer { private Subject weatherData; private float temperature; private float humidity; public CurrentConditionsDisplay(Subject weatherData) { this.weatherData = weatherData; // 註冊觀察者 weatherData.registerObserver(this); } @Override public void update(float temperature, float humidity, float pressure) { this.temperature = temperature; this.humidity = humidity; display(); } public void display() { System.out.println("Current conditions:" + temperature + "F degress and " + humidity + "% humidity"); } }
/** * 統計佈告板 */ public class StatisticsDisplay implements Observer { private Subject weatherData; private ArrayList<Float> historyTemperatures; public StatisticsDisplay(Subject weatherData) { this.weatherData = weatherData; // 註冊觀察者 weatherData.registerObserver(this); historyTemperatures = new ArrayList<>(); } @Override public void update(float temperature, float humidity, float pressure) { this.historyTemperatures.add(temperature); display(); } public void display() { if (historyTemperatures.isEmpty()) { return; } Collections.sort(historyTemperatures); float avgTemperature = 0; float maxTemperature = historyTemperatures.get(historyTemperatures.size() - 1); float minTemperature = historyTemperatures.get(0); float totalTemperature = 0; for (Float temperature : historyTemperatures) { totalTemperature += temperature; } avgTemperature = totalTemperature / historyTemperatures.size(); System.out.println("Avg/Max/Min temperature:" + avgTemperature + "/" + maxTemperature + "/" + minTemperature); } }
/** * 預測佈告板 */ public class ForecastDisplay implements Observer { private Subject weatherData; private float temperature; private float humidity; private float pressure; public ForecastDisplay(Subject weatherData) { this.weatherData = weatherData; // 註冊觀察者 weatherData.registerObserver(this); } @Override public void update(float temperature, float humidity, float pressure) { this.temperature = temperature; this.humidity = humidity; this.pressure = pressure; display(); } public void display() { System.out.println("Forecast:waiting for implementation..."); } }
public class Test { public static void main(String[] args) { // 氣象數據 WeatherData weatherData = new WeatherData(); // 目前狀態佈告板 CurrentConditionsDisplay currentConditionsDisplay = new CurrentConditionsDisplay(weatherData); // 統計佈告板 StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData); // 預測佈告板 ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData); // 模擬氣象觀測值變化 weatherData.setMeasurements(80, 65, 30.4F); weatherData.setMeasurements(82, 70, 29.2F); weatherData.setMeasurements(78, 90, 29.2F); } }