2019年2月18日 星期一

設計模式(2) 觀察者模式 (Observer Pattern)

此篇介紹GoF設計模式(Design Pattern)的其中一種—觀察者模式(Observer Pattern)。


適用問題:
  試想現在要設計一個提供天氣資訊的app,當中可能會需要這樣的函式:
void measurementsChanged(){
    float temp = getTemperature();
    float humidity = getHumidity();
    float pressure = getPressure();

    currentConditionDisplay.update(temp, humidity, pressure);
    statisticsDisplay.update(temp, humidity, pressure);
    forecastDisplay.update(temp, humidity, pressure);
}
  首先我們需要獲取目前的氣溫、濕度、氣壓等資訊,接著要將這些資訊顯示,但這樣的寫法有著缺點,那就是若我們要增加顯示就又必須寫一個函式讓顯示器進行更新,只能修改程式而不能在run-time下新增或減少,顯然不是太好的寫法。

  想想訂報紙的情況,報商會每天印製報紙,客戶則向報商進行訂閱,報商有義務每天送報紙給客戶,客戶若不想再看則也能取消訂閱,而這就是觀察者模式。(Publisher + Subscriber = Observer Pattern)



  觀察者模式定義:
  "The Observer Pattern defines a one-to-many dependency between objects so that when one object changes state, all of its dependents are notified and updated automatically" (觀察者模式定義了一種一對多的依賴關係,讓多個觀察者物件同時監聽某一個主題物件。這個主題物件狀態發生變化時,會通知所有觀察者物件使它們能自動更新自己)

  觀察者模式原則:
  1. Strive for loosely coupled designs between objects that interact. (發佈者與訂閱者沒有強耦合,不清楚彼此細節仍能正常通訊)


  觀察者模式基本UML: Subject -> Observer 的關係是一對多。
  如此的設計當有新的訂閱者出現時,發佈者的程式碼不需要做任何修改,同樣發佈者需要改變時,也不會影響到之前的訂閱者。

範例:
  上面的氣象資訊例子。

  Subject's code (C++):
// The Abstract Subject

class ISubject{
private:
    float        m_humidity;
    float        m_temperature;
    float        m_pressure;
    list<iobserver> m_obs;

public:
    virtual void registerOb(IObserver* ob) = 0;
    virtual void removeOb(IObserver* ob) = 0;
    virtual void notifyOb() = 0;
};

// The Concrete Subject
class WeatherData: public ISubject{
public:
    void SensorDataChange(float a,float b,float c){
        m_humidity = a;
        m_temperature = b;
        m_pressure = c;
        notifyOb();
    }

    void registerob(IObserver* ob){
        m_obs.push_back(ob);
    }
    void removeob(IObserver* ob){
        m_obs.remove(ob);
    }

protected:
    void notifyOb(){
        list<iobserver>::iterator pos = m_obs.begin();
        while (pos != m_obs.end()){
            ((IObserver* )(*pos))->update(m_humidity,m_temperature,m_pressure);
            (dynamic_cast<IDisplay*>(*pos))->show();
            ++pos;
        }
    }
}




  參考資料:
    1. Head First Design Patterns
    2. JavaScript設計模式與開發實踐 (博碩出版)



沒有留言:

張貼留言