接下里介紹的是Java 的設計模式之一:觀察者模式
segmentfault
咱們仍是以一個問題進行展開,引入觀察者模式設計模式
如今有一個天氣預報項目,它的具體要求以下ide
1.氣象站能夠將天天測量到的溫度,溼度,氣壓等等以公告的形式發佈出去(好比發佈到本身的網站或第三方)網站
2.須要設計開放型 API,便於其餘第三方也能接入氣象站獲取數據this
3.提供溫度、氣壓和溼度的接口spa
4.測量數據更新時,要能實時的通知給第三方設計
那麼你會怎麼製做這個項目呢?code
咱們設計類提供天氣(溫度、溼度、氣壓等)的相關數據:WeatherDataserver
推送時,在dataChange方法裏維護公告板、新浪等網站進行更新數據對象
接下來咱們使用普通方法是怎麼解決問題的,同時看看會有什麼問題
咱們先建立氣象臺公告板的示意代碼
/** *顯示當前天氣狀況(能夠理解成是氣象站本身的網站) *@author Administrator * */ class CurrentConditions { // 溫度,氣壓,溼度 private float temperature; private float pressure; private float humidity; //更新 天氣狀況,是由 WeatherData 來調用,我使用推送模式 public void update(float temperature, float pressure, float humidity) { this.temperature = temperature; this.pressure = pressure; this.humidity = humidity; display(); } //顯示 public void display() { System.out.println("***Today mTemperature: " + temperature + "***"); System.out.println("***Today mPressure: " + pressure + "***"); System.out.println("***Today mHumidity: " + humidity + "***"); } }
接下里咱們建立包含天氣狀況信息的WeatherData
/** * 類是核心 * 1. 包含最新的天氣狀況信息 * 2. 含有 CurrentConditions 對象 * 3. 當數據有更新時,就主動的調用 CurrentConditions 對象 update 方法(含 display), 這樣他們(接入方)就看到最新的信息 * * @author Administrator */ class WeatherData { private float temperatrue; private float pressure; private float humidity; //加入新的第三方 private CurrentConditions currentConditions; public WeatherData(CurrentConditions currentConditions) { this.currentConditions = currentConditions; } public float getTemperature() {return temperatrue;} public float getPressure() { return pressure;} public float getHumidity() {return humidity;} public void dataChange() { //調用 接入方的 update currentConditions.update(getTemperature(), getPressure(), getHumidity()); } //當數據有更新時,就調用 setData public void setData(float temperature, float pressure, float humidity) { this.temperatrue = temperature; this.pressure = pressure; this.humidity = humidity; //調用 dataChange, 將最新的信息 推送給 接入方 currentConditions dataChange(); } }
將氣象臺傳入給天氣狀況,當有數據更新時則調用相應的方法也更新氣象臺
public static void main(String[] args) { //建立接入方 currentConditions CurrentConditions currentConditions = new CurrentConditions(); //建立 WeatherData 並將 接入方 currentConditions 傳遞到 WeatherData 中 WeatherData weatherData = new WeatherData(currentConditions); //天氣狀況變化 System.out.println("============天氣狀況變化============="); weatherData.setData(40, 160, 20); } 運行結果以下: ============天氣狀況變化============= ***Today mTemperature: 40.0*** ***Today mPressure: 160.0*** ***Today mHumidity: 20.0***
其餘第三方接入氣象站獲取數據的問題
沒法在運行時動態的添加第三方 (新浪網站)
違反 ocp 原則,在 WeatherData 中,當增長一個第三方,都須要建立一個對應的第三方的公告板對象,並加入到 dataChange, 不利於維護,也不是動態加入
觀察者模式其實像是訂牛奶的業務,一個品牌牛奶對應多個客戶
觀察者模式:對象之間多對一依賴的一種設計方案
,被依賴的對象爲Subject,依賴的對象爲 Observer,Subject通知 Observer 變化,好比這裏的奶站是 Subject,是 1 的一方。用戶時 Observer,是多的一方
根據思路,建立被依賴的Subject接口提供觀察者加入、移出、通知方法
//接口, 讓 WeatherData 來實現 interface Subject { //將觀察者加入進來 public void registerObserver(Observer o); //將觀察者移出出去 public void removeObserver(Observer o); //通知觀察者信息 public void notifyObservers(); }
根據思路,建立觀察者方法,進行實施更新
//觀察者接口,有觀察者來實現 interface Observer { public void update(float temperature, float pressure, float humidity); }
再建立具體的觀察者,實現觀察者接口
class BaiduSite implements Observer { // 溫度,氣壓,溼度 private float temperature; private float pressure; private float humidity; // 更新 天氣狀況,是由 WeatherData 來調用,我使用推送模式 @Override public void update(float temperature, float pressure, float humidity) { this.temperature = temperature; this.pressure = pressure; this.humidity = humidity; display(); } // 顯 示 public void display() { System.out.println("===百度網站===="); System.out.println("***百度網站 氣溫 : " + temperature + "***"); System.out.println("***百度網站 氣壓: " + pressure + "***"); System.out.println("***百度網站 溼度: " + humidity + "***"); } }
接下來建立具體的實現類,完成觀察者的加入、移出、通知等方法
/** *類是核心 *1. 包含最新的天氣狀況信息 *2. 含有 觀察者集合,使用 ArrayList 管理 *3. 當數據有更新時,就主動的調用 ArrayList, 通知全部的(接入方)就看到最新的信息 *@author Administrator * */ class WeatherData implements Subject { private float temperatrue; private float pressure; private float humidity; //觀察者集合 private ArrayList<Observer> observers; //加入新的第三方 public WeatherData() { observers = new ArrayList<Observer>(); } public float getTemperature() { return temperatrue;} public float getPressure() {return pressure;} public float getHumidity() { return humidity; } public void dataChange() { //調用 接入方的 update notifyObservers(); } //當數據有更新時,就調用 setData public void setData(float temperature, float pressure, float humidity) { this.temperatrue = temperature; this.pressure = pressure; this.humidity = humidity; //調用 dataChange, 將最新的信息 推送給 接入方 currentConditions dataChange(); } //註冊一個觀察者 @Override public void registerObserver(Observer o) { observers.add(o); } //移除一個觀察者 @Override public void removeObserver(Observer o) { if (observers.contains(o)) { observers.remove(o); } } //遍歷全部的觀察者,並通知 @Override public void notifyObservers() { for (int i = 0; i < observers.size(); i++) { observers.get(i).update(this.temperatrue, this.pressure, this.humidity); } } }
接下里使用demo 體會觀察者模式下的氣象臺與天氣狀況
public static void main(String[] args) { //建立一個 WeatherData WeatherData weatherData = new WeatherData(); //建立百度觀察者 BaiduSite baiduSite = new BaiduSite(); // 注 冊 到 weatherData weatherData.registerObserver(baiduSite); // 測 試 System.out.println("通知各個註冊的觀察者, 看看信息"); weatherData.setData(10f, 100f, 30.3f); } 運行結果以下: 通知各個註冊的觀察者, 看看信息 ===百度網站==== ***百度網站 氣溫 : 10.0*** ***百度網站 氣壓: 100.0*** ***百度網站 溼度: 30.3***
觀察者模式設計後,會以集合的方式來管理用戶(Observer),包括註冊,移除和通知。
這樣,咱們增長觀察者(這裏能夠理解成一個新的公告板),就不須要去修改核心類 WeatherData 不會修改代碼, 遵照了 ocp 原則
尚硅谷:設計模式(韓順平老師):觀察者模式
Refactoring.Guru:《深刻設計模式》