我所知道設計模式之觀察者模式

接下里介紹的是Java 的設計模式之一:觀察者模式segmentfault

咱們仍是以一個問題進行展開,引入觀察者模式設計模式

如今有一個天氣預報項目,它的具體要求以下ide

1.氣象站能夠將天天測量到的溫度,溼度,氣壓等等以公告的形式發佈出去(好比發佈到本身的網站或第三方)網站

2.須要設計開放型 API,便於其餘第三方也能接入氣象站獲取數據this

3.提供溫度、氣壓和溼度的接口spa

4.測量數據更新時,要能實時的通知給第三方設計

那麼你會怎麼製做這個項目呢?code

1、普通方案解決問題

咱們設計類提供天氣(溫度、溼度、氣壓等)的相關數據:WeatherDataserver

圖片.png

  • 經過getXxx方法,可讓第三方接入,並獲得相關信息
  • 當數據有更新時,氣象站經過調用dataChange()去更新數據
  • 當第三方再次獲取時,就能獲得最新數據,固然也能夠推送。

圖片.png

推送時,在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, 不利於維護,也不是動態加入

2、什麼是觀察者模式

觀察者模式其實像是訂牛奶的業務,一個品牌牛奶對應多個客戶

  • 奶站/氣象局:Subject
  • 用戶/第三方網站:Observer

圖片.png

  • registerObserver 注 冊
  • removeObserver 移 除
  • notifyObservers() 通知全部的註冊的用戶,根據不一樣需求,能夠是更新數據,讓用戶來取,也多是實施推送, 看具體需求定

觀察者模式:對象之間多對一依賴的一種設計方案,被依賴的對象爲Subject,依賴的對象爲 Observer,Subject通知 Observer 變化,好比這裏的奶站是 Subject,是 1 的一方。用戶時 Observer,是多的一方

3、使用觀察者模式解決問題

圖片.png

根據思路,建立被依賴的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:《深刻設計模式》

相關文章
相關標籤/搜索