觀察者模式(Observer Pattern)

觀察者模式:在物件之間定義一個主題和多個觀察者,當主題的狀態改變,觀察者都會收到通知,並自行更新。

用訂報紙的例子來說明觀察者模式,報紙由報社生產,當我們想要訂閱時會跟報社說要訂報紙,當我們不想訂的時候就跟報社說不繼續訂了,報社會主動發送最新的新聞給所有訂閱者,在這個例子裡,把報社當成主題,客戶當成觀察者,而主題 + 觀察者 = 觀察者模式。

實作上我們定義Subject類別,要求繼承的類別必需實作出註冊、移除、通知方法,定義Observer類別,要求繼承的類別必須實作更新方法,當有新的觀察者想要加入此主題時,主題呼叫registerObserver()方法把此觀察者加入名單,如果某個觀察者想退出這個主題時,主題呼叫removeObserver()將此觀察者從名單中移除,如果Subject有新消息要通知名單上的觀察者時,主題呼叫notifyObservers()方法,此方法會依序執行所有觀察者的update()方法,觀察者模式將主題與觀察者兩者的相依性鬆綁,主題不知道觀察者實際上如何更新,而觀察者也不知道有那些其他的觀察者,以及主題如何註冊、移除觀察者的。

#include <iostream>
#include <list>
#include <string>
#include <algorithm>
using namespace std;

class Observer{
public:
    virtual void update(string message) = 0;
};
class Subject{
public:
    virtual void registerObserver(Observer *) = 0;
    virtual void removeObserver(Observer *) = 0;
    virtual void notifyObservers(string message) = 0;
};

class NewsOffice : public Subject{
private:
    list m_observesList;
public:
    void registerObserver(Observer *observer);
    void removeObserver(Observer *observer);
    void notifyObservers(string message);
};
class Customer : public Observer{
private:
    string m_name;
public: 
    Customer(string name){m_name = name;}
    void update(string message);
};

void NewsOffice::registerObserver(Observer *observer){
    m_observesList.push_back(observer);
}
void NewsOffice::removeObserver(Observer *observer){
    list::iterator itr = find(m_observesList.begin(), m_observesList.end(), observer);
    if (itr != m_observesList.end()){
        m_observesList.remove(*itr);
    }
}
void NewsOffice::notifyObservers(string message){
    list::iterator listBegin = m_observesList.begin();
    list::iterator listEnd = m_observesList.end();
    while (listBegin != listEnd){
        (*listBegin)->update(message);
        listBegin++;
    }
}
void Customer::update(string message){
    cout << m_name << "收到" << message << endl;
}
int main() {
    NewsOffice office;
    Observer *bill = new Customer("Bill");
    Observer *mike = new Customer("Mike");
    office.registerObserver(bill);
    office.registerObserver(mike);
    office.notifyObservers("一封訊息");

    office.removeObserver(bill);
    office.notifyObservers("一封新訊息");
    delete bill;
    delete mike;
    system("PAUSE");
    return 0;
}