什麼是觀察者模式 舉個例子來簡單說明下這個模式:假如現在你在一家報社訂閱了報紙,每當有新的期刊,那麼他們就會把報紙送到你家,如果你什麼時候不想看這一期刊的時候,你就可以取消訂閱,那麼這時候他們就不會將報紙送到你家了。這其實就是利用了觀察者模式,先給出兩個基本概念:主題,就相當於被觀察的對象,這裡指的 ...
什麼是觀察者模式
舉個例子來簡單說明下這個模式:假如現在你在一家報社訂閱了報紙,每當有新的期刊,那麼他們就會把報紙送到你家,如果你什麼時候不想看這一期刊的時候,你就可以取消訂閱,那麼這時候他們就不會將報紙送到你家了。這其實就是利用了觀察者模式,先給出兩個基本概念:主題,就相當於被觀察的對象,這裡指的就是報社;觀察者,實時接收主題新的數據,這裡指的就是訂閱報紙的人。
深入理解觀察者模式
先給出一個問題,然後我們就根據這個問題進行討論。
問題:現在有個氣象站,給一些氣象顯示器實時發送數據,氣象顯示器所顯示的數據方式不一樣,有些可能著重溫度或者濕度,客戶也可以根據自己的需求來開發一份氣象顯示器。
根據這個問題,你的代碼可能是這樣的:
以下代碼不是一個文件
//TemperatureDisplay.java public class TemperatureDisplay { public void update(float temperature, float humidity, float pressure) { System.out.println("temperature is " + temperature); } } //HumidityDisplay.java public class HumidityDisplay { public void update(float temperature, float humidity, float pressure) { System.out.println("humidity is " + humidity); } } //MixDisplay.java public class MixDisplay { public void update(float temperature, float humidity, float pressure) { System.out.println("temperature is " + temperature); System.out.println("humidity is " + humidity); System.out.println("pressure is " + pressure); } } //WeatherData.java public class WeatherData { private float temperature; private float humidity; private float pressure; private TemperatureDisplay temperatureDisplay; private HumidityDisplay humidityDisplay; private MixDisplay mixDisplay; public WeatherData() { } public WeatherData(TemperatureDisplay temperatureDisplay, HumidityDisplay humidityDisplay, MixDisplay mixDisplay) { super(); this.temperatureDisplay = temperatureDisplay; this.humidityDisplay = humidityDisplay; this.mixDisplay = mixDisplay; } public float getTemperature() { return temperature; } public void setTemperature(float temperature) { this.temperature = temperature; } public float getHumidity() { return humidity; } public void setHumidity(float humidity) { this.humidity = humidity; } public float getPressure() { return pressure; } public void setPressure(float pressure) { this.pressure = pressure; } //當氣象數據變更時調用 public void measurementsChange() { temperatureDisplay.update(temperature, humidity, pressure); humidityDisplay.update(temperature, humidity, pressure); mixDisplay.update(temperature, humidity, pressure); } }
這可能是你最初的想法,或許你的想法比這個更好
我們來仔細看下上面的代碼,你會發現這個代碼的擴展性很差。如果有人想自定義一個氣象顯示器,那麼你必須修改WeatherData這個類中的代碼,顯然這個代碼是不可以讓其他人隨便進行修改的,因為這個是氣象站提供的。所以氣象站的設計人員肯定就需要重構這一代碼。
解決方案:
我們發現這個氣象站就好像是主題一樣,而氣象顯示器就貌似是觀察者。你們是怎麼看的呢?
看看氣象顯示器的代碼,發現都只有一個update方法,當然你可以添加其他的方法,那麼我們就可以定義一個介面Display,讓所有的氣象顯示器都實現這個介面。然後在WeatherData類中添加一個集合屬性,這個集合中包含的是Display這一類型的對象。
為了可擴展性,可能有很多氣象站,所以我們定義一個Subject的介面,該介面中主要有三個方法,分別是註冊氣象顯示器、刪除註冊氣象顯示器、通知註冊氣象顯示器,即registerObverse、removeObverse、notifyObverse。而每個氣象顯示器中增加一個都必須有一個Subject類型的屬性,並且在構造方法中需要傳遞一個Subject類型的參數進行實例化這個對象,然後這個氣象顯示器在這個主題中進行註冊。
根據這個方案,示例代碼如下:
//Subject.java public interface Subject { void registerObverse(); void removeObverse(); void notifyObverse(); } //Display.java //這就是一個觀察者類的介面 public interface Display { void update(float temperature, float humidity, float pressure); } //WeatherData.java import java.util.ArrayList; import java.util.List; public class WeatherData implements Subject { private float temperature; private float humidity; private float pressure; private List<Display> displays; public WeatherData() { displays = new ArrayList<>(); } public float getTemperature() { return temperature; } public void setTemperature(float temperature) { this.temperature = temperature; } public float getHumidity() { return humidity; } public void setHumidity(float humidity) { this.humidity = humidity; } public float getPressure() { return pressure; } public void setPressure(float pressure) { this.pressure = pressure; } @Override public void registerObverse(Display display) { displays.add(display); } @Override public void removeObverse(Display display) { int i = displays.indexOf(display); if(i>=0) displays.remove(display); } @Override public void notifyObverse() { for(Display display:displays){ display.update(temperature, humidity, pressure); } } //當氣象數據變化時就修改調用這一方法 public void measurementsChanged(){ notifyObverse(); } } //MixDisplay.java //這裡寫了一個氣象顯示器,其他的跟著類似 public class MixDisplay implements Display { private Subject subject; public MixDisplay(Subject subject) { this.subject = subject; subject.registerObverse(this); } public void remove() { subject.removeObverse(this); } @Override public void update(float temperature, float humidity, float pressure) { System.out.println("temperature is " + temperature); System.out.println("humidity is " + humidity); System.out.println("pressure is " + pressure); } }
這時候如果你想自定義一個顯示器,只需要實現Display這一介面就可以了,然後在氣象顯示器中定義一個Subject類型的屬性,然後在構造是進行註冊該觀察者,那麼只要氣象數據變化了你就可以接受到。這是不是具有很好的可擴展性呢?
其實呢Java中內置中就包含了這一模式,其主題就是Observable(這是一個類),觀察者則是Obverse(這是一個介面)。
所以你可以試試用內置的這一模式實現下以上案例。
如果你對該博客有什麼見解或疑問,可以留言哦>v<