【Java】觀察者模式總結
阿新 • • 發佈:2019-02-09
一、 總體大綱
二、觀察者模式定義
出版者 + 訂閱者 = 觀察者模式
觀察者模式:定義了物件之間的一對多依賴,這樣一來,當一個物件改變狀態時,它的所有依賴者都會受到通知並自動更新。
三、設計原則
為了互動物件之間的鬆耦合設計而努力
四、例項說明
1. 實現觀察者模式
實現氣象站 1. 建立介面 public interface Subject { // 這兩個方法都需要一個觀察者作為變數,該觀察者是用來註冊或被刪除 public void registerObserver(Observer o); public void removeObserver(Observer o); // 當主題狀態改變時,這個方法會被呼叫,以通知所有的觀察者 public void notifyObservers(); } // 所有的觀察者都必須實現update()方法,以實現觀察者介面 public interface Observer { public void update(float temp, float humidity, float pressure); } // 當佈告板需要顯示時,呼叫方法display() public interface DisplayElement { public void display(); } 2. 實現主題介面 public class WeatherData implements Subject { // ArrayList記錄觀察者 private ArrayList observers; private float temperature; private float humidity; private float pressure; public WeatherData() { observers = new ArrayList(); } // 當註冊觀察者時,我們只要把它加到ArrayList的後面即可 public void registerObserver(Observer o) { observers.add(o); } // 當觀察者想取消註冊,我們把它從ArrayList中刪除即可 public void removeObserver(Observer o) { int i = observers.indexOf(o); if (i >= 0) { observers.remove(i); } } // 在這裡,我們把狀態告訴每一個觀察者。因為觀察者都實現了update(),所以我們知道如何通知它們 public void notifyObservers() { for (int i = 0; i < observers.size(); i++) { Observer observer = (Observer)observers.get(i); observer.update(temperature, humidity, pressure); } } // 當從氣象站得到更新觀測值時,我們通知觀察者 public void measurementsChanged() { notifyObservers(); } public void setMeasurements(float temperature, float humidity, float pressure) { this.temperature = temperature; this.humidity = humidity; this.pressure = pressure; measurementsChanged(); } public float getTemperature() { return temperature; } public float getHumidity() { return humidity; } public float getPressure() { return pressure; } } 3. 建立觀察者,即佈告板 public class CurrentConditionsDisplay implements Observer, DisplayElement { private float temperature; private float humidity; private Subject weatherData; // 構造器需要weatherData物件(也就是主題)作為註冊之用 public CurrentConditionsDisplay(Subject weatherData) { this.weatherData = weatherData; weatherData.registerObserver(this); } // 當update()被呼叫時,我們把文件和溼度儲存起來,然後呼叫display() 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 degrees and " + humidity + "% humidity"); } } 4. 建立測試程式 public class WeatherStation { public static void main(String[] args) { // 首先建立一個WeatherData物件(即主題) WeatherData weatherData = new WeatherData(); // 建立三個佈告板,並把WeatherData物件傳給它們 CurrentConditionsDisplay currentDisplay = 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); } } 5. 執行程式的結果 ==== Current conditions: 80.0F degrees and 65.0% humidity Avg/Max/Min temperature = 80.0/80.0/80.0 Forecast: Improving weather on the way! Current conditions: 82.0F degrees and 70.0% humidity Avg/Max/Min temperature = 81.0/82.0/80.0 Forecast: Watch out for cooler, rainy weather Current conditions: 78.0F degrees and 90.0% humidity Avg/Max/Min temperature = 80.0/82.0/78.0 Forecast: More of the same ====
2. Java內建觀察者模式
1. 實現主題 // 記得匯入正確的Observable和Observer import java.util.Observable; import java.util.Observer; // 繼承Observable,這裡不需要管理註冊與刪除,讓超類代勞即可 public class WeatherData extends Observable { private float temperature; private float humidity; private float pressure; public WeatherData() { } public void measurementsChanged() { // notifyObservers(Object arg) 該方法是主題主動推送資訊給觀察者,arg為資料物件 // 在呼叫notifyObservers()之前,呼叫setChanged()指示狀態以及改變 setChanged(); notifyObservers(); } public void setMeasurements(float temperature, float humidity, float pressure) { this.temperature = temperature; this.humidity = humidity; this.pressure = pressure; measurementsChanged(); } // 下面的三個方法,如果使用“拉”的做法,觀察者會利用這些方法取得WeatherData物件的狀態 public float getTemperature() { return temperature; } public float getHumidity() { return humidity; } public float getPressure() { return pressure; } } 2. 實現觀察者(即目前狀況佈告板) import java.util.Observable; import java.util.Observer; public class CurrentConditionsDisplay implements Observer, DisplayElement { Observable observable; private float temperature; private float humidity; // 構造器需要Observable當引數,並將CurrentConditionsDisplay物件登記成為觀察者 public CurrentConditionsDisplay(Observable observable) { this.observable = observable; observable.addObserver(this); } // 增加Observable和資料物件作為引數 // 先確定主題Observable屬於WeatherData型別,然後利用getter方法獲取文件和溼度測量值,最後呼叫display() public void update(Observable obs, Object arg) { if (obs instanceof WeatherData) { WeatherData weatherData = (WeatherData)obs; this.temperature = weatherData.getTemperature(); this.humidity = weatherData.getHumidity(); display(); } } public void display() { System.out.println("Current conditions: " + temperature + "F degrees and " + humidity + "% humidity"); } } 3. 測試程式 public class WeatherStation { public static void main(String[] args) { WeatherData weatherData = new WeatherData(); CurrentConditionsDisplay currentConditions = 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); } } 4. 執行程式結果 ==== Forecast: Improving weather on the way! Avg/Max/Min temperature = 80.0/80.0/80.0 Current conditions: 80.0F degrees and 65.0% humidity Forecast: Watch out for cooler, rainy weather Avg/Max/Min temperature = 81.0/82.0/80.0 Current conditions: 82.0F degrees and 70.0% humidity Forecast: More of the same Avg/Max/Min temperature = 80.0/82.0/78.0 Current conditions: 78.0F degrees and 90.0% humidity ====
3. 觀察者與Swing
// 建立一個JFrame,然後放上一個按鈕 public class SwingObserverExample { JFrame frame; public static void main(String[] args) { SwingObserverExample example = new SwingObserverExample(); example.go(); } public void go() { frame = new JFrame(); JButton button = new JButton("Should I do it?"); // 製造兩個觀察者 button.addActionListener(new AngelListener()); button.addActionListener(new DevilListener()); frame.getContentPane().add(BorderLayout.CENTER, button); // Set frame properties 設定屬性 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.getContentPane().add(BorderLayout.CENTER, button); frame.setSize(300,300); frame.setVisible(true); } // 觀察者定義成內部類 // 當主題(JButton)的狀態改變時,呼叫actionPerformed class AngelListener implements ActionListener { public void actionPerformed(ActionEvent event) { System.out.println("Don't do it, you might regret it!"); } } class DevilListener implements ActionListener { public void actionPerformed(ActionEvent event) { System.out.println("Come on, do it!"); } } }