假如咱們有一個開發需求——建造一個氣象觀測站展現系統。需求方給咱們提供了一個WeatherObject對象,可以自動得到最新的測量數據。而咱們要創建一個應用,有三種佈告版,分別顯示目前的情況,氣象統計,簡單預報。三種佈告板能即時顯示WeatherObject對象中更新的數據。java
同時,咱們須要這是一個可擴展的氣象站,能夠公佈一組api,好讓其餘開發人員寫出本身的佈告板插入此應用中。編程
咱們首先來看看咱們的大體系統框架設計模式
咱們的工做就算創建一個應用,利用weatherData對象取得數據,並更新佈告板。api
根據weatherData源文件,咱們的工做是實現measurementChanged(),當測量數據備妥時,measurementChanged()方法將會被調用。框架
先來看一個可能的實現,ide
很明顯,這個實現並不穩當。回想第一章的OO原則,會發現咱們在針對具體實現編程,這會致使當有新需求時咱們必須修改程序。同時,更新佈告板的代碼會常常改變,咱們應該儘量將其封裝。this
接下來咱們將應用觀察者模式來改進現有設計。設計
以報紙訂閱爲例,咱們像某家報社訂閱報紙,只要他們有新報紙第一版,就會給派送給訂戶。而訂戶不想要了,就能夠取消訂閱。只要報社還在運營,就不斷有人訂閱或者取消訂閱報紙。code
出版者+訂閱者=觀察者模式server
若是瞭解了報紙訂閱是怎麼回事,觀察者模式也大致如此。出版者即爲觀察者模式中的主題(Subject),訂閱者即爲觀察者模式中的觀察者(Observer)。
定義觀察者模式
觀察者模式定義了對象之間的一對多依賴,這樣一來,當一個對象改變狀態時,它的全部依賴着都會收到通知並自動更新。
類圖如圖所示
觀察者模式提供了一種對象設計,讓主題和觀察者之間鬆耦合。
對於觀察者,主題只關心觀察者實現了Observer接口,主題並不關心觀察者的細節。
任什麼時候候均可以新增觀察者,由於主題惟一以來的東西是一個實現了Observer接口的對象列表,同時,也能夠在任什麼時候候刪除觀察者。
改變主題或觀察者中的其中一方,並不會影響到另外一方。
設計原則
設計應該儘量下降交互對象之間的耦合度
依照觀察者模式,獲得咱們的新設計
當前,咱們暫時不用Java內置的觀察者模式,而是本身實現這部分代碼。
public interface Observer { public void update(float temp,float humidity,float pressure); }
public interface Subject { public void notifyObserver(); public void removeObserver(Observer observer); public void registerObserver(Observer observer); }
public interface DisplayElement { public void display(); }
import java.util.ArrayList; import java.util.List; public class WeatherData implements Subject { private List<Observer> Observers; private float temperature; private float humidity; private float pressure; public WeatherData() { Observers = new ArrayList<Observer>(); } @Override public void notifyObserver() { for (Observer o: Observers ) { o.update(temperature,humidity,pressure); } } @Override public void removeObserver(Observer observer) { Observers.remove(observer); } @Override public void registerObserver(Observer observer) { Observers.add(observer); } public void measurementChanged() { notifyObserver(); } public void setMeasurements(float temperature,float humidity,float pressure) { this.temperature = temperature; this.humidity = humidity; this.pressure = pressure; measurementChanged(); } }
public class CurrentConditionsDisplay implements Observer,DisplayElement{ private float temperature; private float humidity; private Subject weatherData; @Override public void display() { System.out.println("Current conditions: "+temperature+"F degrees and "+humidity+"% humidity"); } @Override public void update(float temp, float humidity, float pressure) { this.temperature = temp; this.humidity = humidity; display(); } public CurrentConditionsDisplay( Subject weatherData) { this.weatherData = weatherData; weatherData.registerObserver(this); } }
public class WeatherStation { public static void main(String[] args) { WeatherData weatherData = new WeatherData(); CurrentConditionsDisplay currentConditionsDisplay = new CurrentConditionsDisplay(weatherData); weatherData.setMeasurements(80,65,30.4f); } }
使用Java內置的觀察者模式
Java api有自帶的觀察者模式,包含Observer接口和Observable類,在使用上更加方便,不少功能已經事先準備好了。以下是咱們使用Java內置觀察者模式修改後的設計。
註冊/刪除觀察者
調用Observable對象的addObserver方法和deleteObserver方法便可
被觀察者送出通知
首先調用setChanged()方法,標記狀態已經改變,後調用notifyObservers(),那麼全部觀察者都會調用自身的update方法。
import java.util.Observable; import java.util.Observer; public class CurrentConditionsDisplay implements Observer,DisplayElement{ private double temperature; private double humidity; private Observable observable; @Override public void display() { System.out.println("Current temperature "+temperature+"F degrees and "+humidity+"%humidity"); } public CurrentConditionsDisplay(Observable observable) { this.observable = observable; observable.addObserver(this); } @Override public void update(Observable o, Object arg) { if(o instanceof WheatherData) { temperature = ((WheatherData) o).getTemperature(); humidity = ((WheatherData) o).getHumidity(); display(); } } }
import java.util.ArrayList; import java.util.List; import java.util.Observable; public class WheatherData extends Observable { private double temperature; private double humidity; private double pressure; public double getHumidity() { return humidity; } public double getPressure() { return pressure; } public double getTemperature() { return temperature; } public void setMeasurements(double temperature,double humidity,double pressure) { this.temperature = temperature; this.humidity = humidity; this.pressure = pressure; setChanged(); notifyObservers(); } }
不幸的是,observable是一個類而不是接口,致使咱們難以繼承其餘類,同時也沒法擁有本身獨特的實現。
在實際使用時,咱們須要權衡是使用jdk自帶的觀察者模式亦或是由本身實現。