目前咱們須要實現一種天氣實時更新的程序(天氣推送),當氣象站數據更新後,天氣接口程序去獲取最新天氣數據,而後將數據分發給全部訂閱過「天氣日報」程序的用戶,即便更新數據。java
(案例來源於《Head First 設計模式》)設計模式
由上圖能夠看出,氣象站負責去檢測天氣狀況,當數據發生變化時,天氣服務獲取了變化的數據而且須要將數據分發給衆多訂閱「天氣推送」的用戶,若是咱們採用一個個去通知的形式去實現的話,那麼當咱們新增了一個觀察者,咱們又須要單獨重複編寫發佈信息代碼,當咱們想去掉一個觀察者的時候,又須要去刪減部分代碼,這樣的操做實在很繁瑣,耦合度也比較高。接下來咱們採用觀察者模式來實現這個需求,來看看有沒有什麼神奇的地方。測試
觀察者模式定義了一系列對象之間的一對多關係,當一個對象(主題)改變狀態,其餘依賴者都會收到通知。this
抽象主題角色:把全部對觀察者對象的引用保存在一個集合中,每一個抽象主題角色均可以有任意數量的觀察者。抽象主題提供一個接口,能夠增長和刪除觀察者角色。通常用一個抽象類和接口來實現。spa
抽象觀察者角色:爲全部具體的觀察者定義一個接口,在獲得主題的通知時更新本身。.net
具體主題角色:在具體主題內部狀態改變時,給全部登記過的觀察者發出通知。具體主題角色一般用一個子類實現。設計
具體觀察者角色:該角色實現抽象觀察者角色所要求的更新接口,以便使自己的狀態與主題的狀態相協調。一般用一個子類實現。若是須要,具體觀察者角色能夠保存一個指向具體主題角色的引用。code
當數據更新後,主題會將全部更新數據都推送給觀察者,而觀察者只能被動接受主題的推送信息。server
當數據更新後,主題會通知觀察者數據更新了,並暴露出相關數據的getter方法,觀察者們可根據本身的須要去拉取本身須要的信息。對象
這裏咱們採起本身編寫抽象主題與抽象觀察者代碼的方式來實現。
抽象主題:SubjectInterface.java
package observer.one.subject; import observer.one.observers.ObserverInterface; public interface SubjectInterface { public void registerObserver(ObserverInterface o); public void removeObserver(ObserverInterface o); public void notifyObservers(); }
具體主題:WeatherData.java
package observer.one.subject.impl; import java.util.ArrayList; import observer.one.observers.ObserverInterface; import observer.one.subject.SubjectInterface; public class WeatherData implements SubjectInterface{ private ArrayList<ObserverInterface> observers; //溫度 private String temperature; //溼度 private String humidity; //氣壓 private String pressure; public WeatherData() { observers=new ArrayList<ObserverInterface>(); } /** * 訂閱 */ public void registerObserver(ObserverInterface o) { observers.add(o); } /** * 取消訂閱 */ public void removeObserver(ObserverInterface o) { if(observers.indexOf(o)>=0){ observers.remove(o); } } /** * 通知觀察者 */ public void notifyObservers() { for(ObserverInterface o:observers){ o.update(temperature, humidity, pressure); } } public void setDataChange() { notifyObservers(); } /** * 數據改變後,通知觀察者 * @param temperature * @param humidity * @param pressure */ public void setNewData(String temperature,String humidity,String pressure){ this.temperature=temperature; this.humidity=humidity; this.pressure=pressure; setDataChange(); } }
抽象觀察者:ObserverInterface.java
package observer.one.observers; public interface ObserverInterface { void update(String temperature,String humidity,String pressure); }
實際上,咱們看到這裏就會發現有點問題,一旦咱們的數據參數發生變化,就須要去修改抽象觀察者方法,而具體觀察者也須要去修改相關方法的實現,耦合性較大。
具體觀察者:Observer1.java
package observer.one.observers.impl; import java.util.Date; import observer.one.observers.ObserverInterface; import observer.one.subject.SubjectInterface; public class Observer1 implements ObserverInterface{ private SubjectInterface subject; //溫度 private String temperature; //溼度 private String humidity; //氣壓 private String pressure; public Observer1(SubjectInterface subject) { this.subject = subject; subject.registerObserver(this); } public void register(){ System.out.println("------觀察者1訂閱成功-------"); subject.registerObserver(this); } public void cancelRegister(){ System.out.println("------觀察者1取消訂閱了-------"); subject.removeObserver(this); } public void update(String temperature, String humidity, String pressure) { this.humidity=humidity; this.pressure=pressure; this.temperature=temperature; display(); } private void display() { System.out.println("觀察者1----數據更新了("+new Date()+")-溫度:"+temperature+"-溼度:"+humidity+"-氣壓:"+pressure); } }
測試方法:
這裏爲了方便演示,我寫了兩個具體觀察者(Observer1 ,Observer2 ),實際上在具體實現中只須要寫一個,實例化多個便可。
package observer.one; import observer.one.observers.impl.Observer1; import observer.one.observers.impl.Observer2; import observer.one.subject.impl.WeatherData; public class Test { public static void main(String[] args) { WeatherData weatherData=new WeatherData(); //訂閱天氣日報 Observer1 observer1=new Observer1(weatherData); //訂閱天氣日報 Observer2 observer2=new Observer2(weatherData); weatherData.setNewData("10", "20", "30"); observer1.cancelRegister(); weatherData.setNewData("10.5", "14.23", "15.65"); observer1.register(); weatherData.setNewData("15.5", "18.23", "17"); } }
輸出結果:
這裏咱們採起JDK本身提供的抽象主題與抽象觀察者的方式來實現。
抽象主題:java.util.Observable
抽象觀察者:java.util.Observer
具體主題:WeatherData.java
package observer.two.subject; import java.util.Observable; public class WeatherData extends Observable{ //溫度 private String temperature; //溼度 private String humidity; //氣壓 private String pressure; /** * 數據更新方法 * @param temperature * @param humidity * @param pressure */ public void setNewData(String temperature,String humidity,String pressure){ this.temperature=temperature; this.humidity=humidity; this.pressure=pressure; setChanged(); notifyObservers("數據更新了,快來獲取吧"); } public String getTemperature() { return temperature; } public String getHumidity() { return humidity; } public String getPressure() { return pressure; } }
由上面代碼能夠看出,具體主題主動暴露出屬性的getter方法,當數據更新時調用setChange()方法,通知觀察者此時有數據更新,你能夠來獲取了。
具體觀察者:Observer1.java
package observer.two.observers; import java.util.Date; import java.util.Observable; import java.util.Observer; import observer.two.subject.WeatherData; public class Observer1 implements Observer{ private Observable observable; //溫度 private String temperature; //溼度 private String humidity; //氣壓 private String pressure; public Observer1(Observable observable) { super(); this.observable = observable; System.out.println("------觀察者1訂閱了-------"); observable.addObserver(this); } public void cancelRegister(){ System.out.println("------觀察者1取消訂閱了-------"); observable.deleteObserver(this); } /** * 數據更新通知 */ public void update(Observable o, Object arg) { System.out.println("氣象臺說:"+arg+""); System.out.println("觀察者1:嗯,這就去"); if(o instanceof WeatherData){ WeatherData data=(WeatherData)o; this.humidity=data.getHumidity(); this.pressure=data.getPressure(); this.temperature=data.getTemperature(); } display(); System.out.println("數據提取完畢,並已展現"); } /** * 數據打印 */ private void display() { System.out.println("觀察者1----數據更新了("+new Date()+")-溫度:"+temperature+"-溼度:"+humidity+"-氣壓:"+pressure); } }
此時,具體觀察者調用update方法,主動拉取主題的最新數據,並顯示出來。
測試方法:
package observer.two; import observer.two.observers.Observer1; import observer.two.subject.WeatherData; public class Test { public static void main(String[] args) { WeatherData weatherData=new WeatherData(); /*觀察者1訂閱天氣日報*/ Observer1 observer1=new Observer1(weatherData); Observer1 observer2=new Observer1(weatherData); weatherData.setNewData("10", "79", "18"); } }
輸出結果:
細心的同窗應該能夠發現,本身編寫抽象主題代碼時,咱們編寫的接口(interface),而JDK官方提供的觀察者模式中,抽象主題採用的是繼承的形式實現。歸根結底這兩種實現方式的對比,是主題推送數據和觀察者拉取數據的對比,以及實現接口和繼承父類的對比。
推送數據和拉取數據的對比:
推送數據的實現方式主題須要把全部的參數都推送給觀察者們,這裏就須要事先在update方法中指定全部的參數,一旦參數發生改變,不只要改變主題的方法,還須要改變觀察者接口方法。
而拉取數據的實現方式只須要改變主題參數,不須要改變觀察者接口方法,觀察者們依舊根據本身的須要去獲取數據。
實現接口和繼承父類的對比:
因爲JAVA只支持單繼承,實現接口和繼承父類的優劣勢你們也應該都很清楚。
對比結果:若是能夠的話,咱們應該本身去實現本身的抽象主題,而不採用JDK官方提供的實現方式,畢竟繼承的形式有必定的弊端。在數據獲取方面,最好採用具體觀察者拉取的形式,這樣更有利於後期進行擴展。具體的代碼實現,我就不共享出來了,仔細看了這篇文章的人基本均可以本身去實現。
相關文章:《設計模式——策略模式:會員價格體系的簡單實現》