設計模式學習專欄三--------觀察者模式

設計模式學習專欄三--------觀察者模式

場景


創建一個氣象站應用, 利用WeatherData對象取得數據,並更新三個佈告板: 目前情況 , 氣象統計 , 天氣預報java

此係統中的三個部分是氣象站(獲取實際氣象數據的物理裝置) , WeatherData對象(追蹤來自氣象站的數據,並更新佈告板) 和 佈告板(顯示目前天氣情況給用戶看.)git

WeatherData對象知道如何跟物理氣象站聯繫,以取得更新的數據.WeatherData對象會隨即更新三個佈告板的顯示: 目前情況(溫度 , 溼度 , 氣壓), 氣象統計和天氣預報github

剛開始實現方式

//WeatherData構造函數  , 當新公告板加入時, 也要跟着改變
public WeatherData(CurrentConditionsDisplay currentConditionsDisplay,
                       StatisticsDisplay statisticsDisplay,ForecastDisplay forecastDisplay) {
        this.currentConditionsDisplay = currentConditionsDisplay;
        this.statisticsDisplay = statisticsDisplay;
        this.forecastDisplay = forecastDisplay;
    }

如何解決


分析可變部分設計模式

  1. 在weatherData的構造函數中 , 對佈告板的加入與移除
  2. 在收到氣象數據變化時, 須要通知全部已加入的佈告板

觀察者模式總覽


定義:在 對象之間定義一對多的依賴,這樣一來, 當一個對象改變狀態,依賴它的對象都會收到通知,並自動更新
  • 類圖

  • 模式的理解函數

    • 角色學習

      • 主題Subject : 主題對象管理某些數據 , 當主題內的數據改變時 , 就會通知觀察者
      • 觀察者Observer : 觀察者已經訂閱主題, 以便在主題數據改變時能收到更新 , 當不想訂閱主題時,也能隨時移除訂閱
    • 細節this

      • 使用觀察者模式在任意時候都能 增長/刪除新的觀察者,而不修改原有代碼
      • 觀察者模式 對數據推送有兩種方式 推(push)和拉(pull)spa

        • 推送方式:設計

          • 當主題通知變化時, 主題攜帶着數據通知觀察者.
        • 拉取方式code

          • 當主題通知變化時, 觀察者經過主題的引用 , 去獲取主題中的數據信息
    • 觀察者模式提供了一種對象設計,讓主題和觀察者之間鬆耦合

      • 對主題而言, 它只知道觀察者實現了某個接口(Observer). 而不須要知道觀察者的具體實現是誰, 作了些什麼與其餘細節
      • 對觀察者而言, 它不須要關心主題細節, 只知道能夠註冊/移除主題 , 而且主題有數據更新時會調用update方法傳入數據

核心代碼部分

  • 主題

    public interface Subject {
        public void registerObserver(Observer o);
        public void removeObserver(Observer o);
        public void notifyObservers();
    }
  • 觀察者

    public interface Observer {
        public void update(float temp, float humidity, float pressure);
    }
  • WeatherData

    public class WeatherData implements Subject {
        private ArrayList<Observer> observers;
        private float temperature;
        private float humidity;
        private float pressure;
        
        public WeatherData() {
            observers = new ArrayList<Observer>();
        }
        
        public void registerObserver(Observer o) {
            observers.add(o);
        }
        
        public void removeObserver(Observer o) {
            int i = observers.indexOf(o);
            if (i >= 0) {
                observers.remove(i);
            }
        }
        
        public void notifyObservers() {
            //遍歷全部的觀察者,調用update方法
            for (Observer observer : observers) {
                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();
        }
        //其餘的get方法
    }
  • 觀察者--今日佈告板

    public class CurrentConditionsDisplay implements Observer, DisplayElement {
        private float temperature;
        private float humidity;
        private Subject weatherData;    //持有主題引用 , 用於拉取數據/刪除主題
        
        public CurrentConditionsDisplay(Subject weatherData) {
            this.weatherData = weatherData;
            weatherData.registerObserver(this);
        }
        
        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");
        }
    }
  • 主程序 ----主題數據改變

    public class WeatherStationHeatIndex {
    
        public static void main(String[] args) {
            WeatherData weatherData = new 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);
        }
    }
  • 輸出結果

    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

Java內置的觀察者模式

  • 主題 ==> java.util.Observable
  • 觀察者 ==> java.util.Observer
  • 類圖

  • 使用

    • 把對象變成觀察者 , 實現Observer接口, 使用addObserver()或者deleteObserver()進行 訂閱/刪除主題
    • 可觀察者(主題)送出通知

      • 調用setChanged()方法, 標記數據已改變

        setChanged(){
            changed = true;
        }
        
        notifyObservers(Object arg){
            if(changed){            //只有當changed爲true時才進行通知
                for every observer on the list{
                    call update(this,arg)
                }
                changed = false;    //通知觀察者後,將changed設置爲false
            }
        }
        
        notifyObservers(){
            notifyObservers(null);
        }
      • 調用notifyObservers() 通知全部觀察者

        • notifyObservers();
        • notifyObservers(Object args);
    • 觀察者接收通知

      • update(Observable o , Object arg) //拿到可觀察者引用,便可拉取數據
      public void update(Observable obs, Object arg) {
              if (obs instanceof WeatherData) {
                  WeatherData weatherData = (WeatherData)obs;
                  this.temperature = weatherData.getTemperature();
                  this.humidity = weatherData.getHumidity();
                  display();
              }
          }

參考

​ 書籍: HeadFirst設計模式

​ 代碼參考地址: 我就是那個地址

相關文章
相關標籤/搜索