1. 程式人生 > >設計模式之禪(2)-觀察者模式

設計模式之禪(2)-觀察者模式

文章目錄


更多關於設計模式的文章請點選設計模式之禪(0)-目錄頁


觀察者模式是Java標準庫的開發中常用的一種設計模式,它完美的將觀察者和被觀察的物件分離開。當被觀察者(主題)改變時,所有觀察者(訂閱者)都將受到改變的情況,而無論觀察者處於什麼樣的狀態。

一、什麼是觀察者模式

觀察者模式Observer Pattern)完美的將觀察者和被觀察的物件分離開。舉個例子,使用者介面可以作為一個觀察者,業務資料是被觀察者,使用者介面觀察業務資料的變化,發現數據變化後,就顯示在介面上。面向物件設計的一個原則是:系統中的每個類將重點放在某一個功能上,而不是其他方面。一個物件只做一件事情,並且將他做好。觀察者模式在模組之間劃定了清晰的界限,提高了應用程式的可維護性和重用性。

觀察者設計模式定義了物件間的一種一對多的組合關係,以便一個物件的狀態發生變化時,所有依賴於它的物件都得到通知並自動重新整理。

在這裡插入圖片描述

二、通過設計氣象站模組來說明觀察者模式

2.1、觀察者模式設計的一般類圖

在這裡插入圖片描述

2.2、觀察者模式帶來的鬆耦合

當兩個物件之間鬆耦合,它們依然可以互動,但是不太清楚彼此的細節。
觀察者模式提供了一種物件設計,讓主題和觀察者之間鬆耦合。

為什麼呢?

關於觀察者的一切,主題只知道觀察者實現了某個介面(也就是Observer介面)。

主題不需要知道觀察者的具體類是誰、做了些什麼或其他任何細節。
任何時候我們都可以增加新的觀察者。因為主題唯一依賴的東西是一個實現Observer介面的物件列表,所以我們可以隨時增加觀察者。事實上,在執行時我們可以用新的觀察者取代現有的觀察者,主題不會受到任何影響。

同樣的,也可以在任何時候刪除某些觀察者。
有新型別的觀察者出現時,主題的程式碼不需要修改。假如我們有個新的具體類需要當觀察者,我們不需要為了相容新型別而修改主題的程式碼,所有要做的就是在新的類裡實現此觀察者介面,然後註冊為觀察者即可。主題不在乎別的,它只會傳送通知給所有實現了觀察者介面的物件。

我們可以獨立地複用主題或觀察者。如果我們在其他地方需要使用主題或觀察者,可以輕易地複用,因為二者並非緊耦合。

2.3、氣象站模組需求

在這裡插入圖片描述

在這裡插入圖片描述

2.4、使用觀察者模式實現氣象站模組

在這裡插入圖片描述

  • Subject介面
/**
 * @Description: 主題介面
 * @CreateDate: Created in 2018/11/22 11:30
 * @Author: <a href="https://blog.csdn.net/pbrlovejava">arong</a>
 */
public interface Subject {
    //註冊觀察者
    public void registerObserver(Observer observer);
    //移除觀察者
    public void removeObserver(Observer observer);
    //通知所有觀察者
    public void notifyObservers();

}
  • Observer介面
/**
 * @Description:   觀察者的介面
 * @CreateDate: Created in 2018/11/22 11:32
 * @Author: <a href="https://blog.csdn.net/pbrlovejava">arong</a>
 */
public interface Observer {
    //當氣象觀測值改變時,主題會把這些狀態值當作方法的引數,傳送給觀察者。
    public void update(float temp,float humidity,float pressure);
}
  • DisplayElement類
/**
 * @Description: DisplayElement介面只包含了一個方法,也就是display()。當佈告板需要顯示時,呼叫此方法。
 * @CreateDate: Created in 2018/11/22 11:36
 * @Author: <a href="https://blog.csdn.net/pbrlovejava">arong</a>
 */
public interface DisplayElement {
    public void display();
}
  • WeatherData類
/**
 * @Description:
 * @CreateDate: Created in 2018/11/22 11:38
 * @Author: <a href="https://blog.csdn.net/pbrlovejava">arong</a>
 */
public class WeatherData implements Subject {
    private ArrayList<Observer> observers;
    private float temp;
    private float hudimity;
    private float pressure;

    public WeatherData(){
        observers = new ArrayList<Observer>();
    }


    @Override
    public void registerObserver(Observer observer) {
        observers.add(observer);
    }

    @Override
    public void removeObserver(Observer observer) {
        int i = observers.indexOf(observer);
        if(i >= 0){
            observers.remove(observer);
        }else{
            System.out.println("can not delete--該主題沒有被該觀察者訂閱!");
        }
    }

    @Override
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update(temp,hudimity,pressure);
        }
    }

    public void changed(){
        notifyObservers();
    }

    public void setMeasurements(float t,float h,float p){
        this.temp = t;
        this.hudimity = h;
        this.pressure = p;
        changed();
    }
}

  • CurrentConditionDisplay類
/**
 * @Description: 當前天氣狀況公告板
 * @CreateDate: Created in 2018/11/22 11:48
 * @Author: <a href="https://blog.csdn.net/pbrlovejava">arong</a>
 */
public class CurrentConditionDisplay implements Observer,DisplayElement{
    private float temp;
    private float humidity;
    private float pressure;
    private Subject subject;

    public CurrentConditionDisplay(Subject s){
        this.subject = s;
        //在該主題中註冊該觀察者,接受主題釋出的變化
        subject.registerObserver(this);
    }

    @Override
    public void update(float temp, float humidity, float pressure) {
        this.temp = temp;
        this.humidity = humidity;
        this.pressure = pressure;
        //更新完畢,輸出更新後的狀態
        display();
    }

    @Override
    public void display() {
        System.out.println("當前天氣狀況:");
        System.out.println("temp :"+temp+"\t humidity :"+humidity+"\t pressure :"+pressure);
    }
}
  • 測試氣象站模組
/**
 * @Description: 測試觀察者模式構建的氣象站能否使用
 * @CreateDate: Created in 2018/11/22 11:55
 * @Author: <a href="https://blog.csdn.net/pbrlovejava">arong</a>
 */
public class TestWeatherData {
    @Test
    public void TestObserverPattern(){
        WeatherData weatherData = new WeatherData();
        //三個觀察者去訂閱weatherData主題,當主題有所改變,觀察者會接受到改變
        CurrentConditionDisplay conditionDisplay1 = new CurrentConditionDisplay(weatherData);
        CurrentConditionDisplay conditionDisplay2 = new CurrentConditionDisplay(weatherData);
        CurrentConditionDisplay conditionDisplay3 = new CurrentConditionDisplay(weatherData);
        //主題移除掉一個觀察者
        weatherData.removeObserver(conditionDisplay1);
        //更新資料
        weatherData.setMeasurements(1.0f,2.0f,3.0f);
        weatherData.setMeasurements(3.0f,2.0f,1.0f);
    }
}

在這裡插入圖片描述