設計模式 ----- 觀察者模式

設計模式 —– 觀察者模式

個人博客,想要搭建個人博客的可以進來看看: http://www.ioqian.top/


觀察者模式,在對象之間定義一對多的依賴,當一個對象狀態改變時,依賴他的對象就會收到通知。典型的實現是java swing中的組件監聽事件,當我們點擊按鈕時會調用我們註冊的回調函數;還有Rxjava等等

背景
我們是一個天氣臺,有許多客戶,當我們天氣信息更新時,我們需要通知用戶進行刷新…,我們要利用的是觀察者模式

此處輸入圖片的描述

觀察者模式主要由下面幾部分組成:

  • Subject:抽象主題(抽象被觀察者),抽象主題角色把所有觀察者對象保存在一個集合裏,每個主題都可以有任意數量的觀察者,抽象主題提供一個接口,可以增加和刪除觀察者對象
  • WeatherData :具體主題(具體被觀察者),該角色將有關狀態存入具體觀察者對象,在具體主題的內部狀態發生改變時,給所有註冊過的觀察者發送通知
  • Observer:抽象觀察者,是觀察者者的抽象類,它是一個接口,裏面有一個很重要的方法,當具體主題狀態改變時,直接調用這個方法,這裏實現了松耦合
  • BoardCallOne :具體觀察者,是實現抽象觀察者定義的更新接口,以便在得到主題更改通知時更新自身的狀態。

結合我們的背景和下面的代碼,我們天氣臺就是一個具體主題,我們的客戶就是具體觀察者,我們這裏抽象出了兩個接口是爲了實現松耦合

松耦合設計原則

當兩個對象松耦合時,他們可以互相交互,但是不太清楚彼此之間的細節,我們這裏讓主題和觀察者之間松耦合

我們怎麼利用接口實現的松耦合哪?
- 關於觀察者的一切,主題只知道觀察者實現了Observer接口,主題不需要知道具體的觀察者是誰,有什麼細節,只要繼承了Observer接口,有update()實現方法就可以
- 任何時候都可以增加新的觀察者,因爲主題僅僅依賴了Observer接口,我們可以隨時增加或者刪除觀察者
- 有新的觀察者時,主題的代碼不要修改

此處輸入圖片的描述
抽象主題 Subject

public interface Subject {
    //該方法把任意的觀察者加入到觀察者集合
    public void registerObserver(Observer o);
    //該方法把觀察者從觀察者從集合中除去,從此收不到狀態改變
    public void removeObserver(Observer o);
    //當狀態改變時通過觀察者集合中的所以觀察者
    public void notifyAllObserver();
}

抽象觀察者 Observer

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

具體主題 WeatherData

public class WeatherData implements Subject  {
    //觀察者集合
    private List<Observer> observers;
    //模擬自身狀態,當這些狀態改變時通知所有觀察者
    private float temperature;
    private float humidity;

    public WeatherData(){
        observers = new ArrayList<>();
    }
    //模擬狀態改變通知所有觀察者
    public void stateChanged(float temp , float humidity){
        this.temperature = temp;
        this.humidity = humidity;
        notifyAllObserver();
    }
    //模擬狀態改變通知所有觀察者
    public void stateChanged(){
        notifyAllObserver();
    }
    @Override
    public void registerObserver(Observer o) {
        observers.add(o);
    }
    @Override
    public void removeObserver(Observer o) {
        int i = observers.indexOf(o);
        if(i>=0){
            observers.remove(i);
        }
    }
    @Override
    public void notifyAllObserver() {
        for(Observer o : observers){
            //這裏很關鍵,這裏調用的是抽象觀察者的方法,不需要明白誰是觀察者,只要繼承了Observer的類就可以
            o.update(temperature , humidity);
        }
    }
}

具體觀察者 BoardCallOne

public interface DisplayDevice {
    public void display();
}
//繼承DisplayDevice接口和觀察者模式沒什麼聯繫
public class BoardCallOne implements Observer ,DisplayDevice {
    private static final String TAG = "BoardCallOne";
    private float temp;
    private float humidity;
    //抽象主題主題
    private Subject weatherData ;
    public BoardCallOne(Subject weatherData) {
        this.weatherData = weatherData;
        //作爲觀察者,註冊主題
        weatherData.registerObserver(this);
    }
    //取消註冊
    public void removeRegister(){
        weatherData.removeObserver(this);
    }
    //作爲觀察者必須實現的方法,所有當具體主題狀態變化會通知我,我本身調用display()方法,實時刷新天氣信息
    @Override
    public void update(float temp, float humidity) {
        this.temp = temp;
        this.humidity = humidity;
        display();

    }
    @Override
    public void display() {
        System.out.println(TAG + " i get info from weather data {temp="+temp+",humidity="+humidity+"}");
    }
}

測試main

public class Main {
    public static void main(String[] args) throws InterruptedException {
        //具體主題實現這
        WeatherData subject = new WeatherData();
        //具體觀察者,在觀察者的構造方法中註冊了主題
        BoardCallOne boardCallOne = new BoardCallOne(subject);
        //在線程中一直讓主題去通知觀察者10次,每次休息1s
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i = 0 ; i < 10 ;i++){
                    try {
                        subject.stateChanged(10.0f,18.0f);
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        t.start();
        //主線程休眠5s後觀察者取消註冊,所以結果是會打印5次消息通知
        Thread.sleep(5000);
        boardCallOne.removeRegister();
        //等待子線程結束
        t.join();
    }
}
結果驗證了我們的猜測
BoardCallOne i get info from weather data {temp=10.0,humidity=18.0}
BoardCallOne i get info from weather data {temp=10.0,humidity=18.0}
BoardCallOne i get info from weather data {temp=10.0,humidity=18.0}
BoardCallOne i get info from weather data {temp=10.0,humidity=18.0}
BoardCallOne i get info from weather data {temp=10.0,humidity=18.0}

Process finished with exit code 0

總結

1.我們這裏的是push模式,push模式是主題狀態改變去通知所有觀察者;還有pull,觀察者可以主動去獲取主題的狀態

2.觀察者模式用到了那些設計原則
- 變化分離原則,在觀察者模式中,會改變的是主題的狀態,及觀察者的類型和數目,我們可以改變改變依賴於主題的對象,不必改變主題
- 針對接口編程原則,主題和觀察者都使用了接口。觀察者利用主題的接口進行向主題註冊(在代碼具體觀察者的構造函數中);主題利用接口通知觀察者,實現了松偶爾
- 多用組合,少用繼承 ,利用組合把衆多觀察者包含進主題,private List observers;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章