觀察者模式定義對象間的一種一對多的依賴關系,當一個對象的狀態發生改變時,所有依賴於它的對象都得到通知並自動更新。這一模式鎮中關鍵對象是目標(subject)和觀察者(observer)。一個目標可以有任意數目的依賴於它的觀察者。一旦目標的狀態發生改變,所有觀察者都得到通知。作為對這個通知的響應,每個觀察者都將查詢目標以使其狀態與目標的狀態同步。這種交互又稱為發布-訂閱模式。目標是通知的發布者,它發出通知時並不需要知道誰是它的觀察者,可以有任意數目的觀察者訂閱並接收通知。
觀察者模式的適用情況如下:
1)當一個抽象模型有兩個方面,其中一個方面依賴於另一個方面;將這兩者封裝在獨立的對象總以使它們可以各自獨立地改變和復用;
2)當一個對象的改變需要同時改變其他對象,而不知道具體有多少個對象有待改變;
3)當一個對象必須通知其他對象,而它又不能假定其他對象是誰。即我們不希望這些對象是緊密耦合的。
觀察者模式結構圖如下:
觀察者模式涉及到:
1)目標Subject:
目標知道它的觀察者,可以有任意多個觀察者觀察同一個目標;提供注冊和刪除觀察者對象的接口;
2)觀察者Observer:
為那些在目標發生改變時需獲得通知的對象定義一個更新接口;
3)具體目標ConcreteSubject:
將有關狀態存入各ConcreteObserver對象;當它的狀態發生改變時,向它的各個觀察者發出通知;
4)具體觀察者ConcreteObserver:
維護一個指向ConcreteSubject對象的引用;存儲有關狀態,這些狀態應與目標的狀態保持一致;實現Observer的更新接口以使自身狀態與目標的狀態保持一致;
實現:
觀察者接口:
class Subject;
class Observer
{
public:
virtual ~Observer();
virtual void Update(Subject* m_subject) = 0;
protected:
Observer();
};
這種實現方式支持一個觀察者有多個目標,當觀察者觀察多個目標時,作為參數傳遞給Update操作的目標讓觀察者可以判定是哪一個目標發生了改變。
目標接口:
class Subject
{
public:
virtual ~Subject();
virtual void Attach(Observer*);
virtual void Detach(Observer*);
virtual void Notify();
protected:
Subject();
private:
std::list<Observer*> *_observers;
};
Subject::Subject()
{
//記得使用前初始化
_observers = new std::list<Observer*>;
}
void Subject::Attach(Observer* observer)
{
_observers->push_back(observer);
}
void Subject::Detach(Observer* observer)
{
if(observer != NULL)
_observers->remove(observer);
}
void Subject::Notify()
{
std::list<Observer*>::iterator it = _observers->begin();
while(it != _observers->end())
{
(*it)->Update(this);
++it;
}
}
下面的例子是從維基百科上得來的:
#include <list>
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
// The Abstract Observer
class ObserverBoardInterface
{
public:
virtual void update(float a,float b,float c) = 0;
};
// Abstract Interface for Displays
class DisplayBoardInterface
{
public:
virtual void show() = 0;
};
// The Abstract Subject
class WeatherDataInterface
{
public:
virtual void registerob(ObserverBoardInterface* ob) = 0;
virtual void removeob(ObserverBoardInterface* ob) = 0;
virtual void notifyOb() = 0;
};
// The Concrete Subject
class ParaWeatherData: public WeatherDataInterface
{
public:
void SensorDataChange(float a,float b,float c)
{
m_humidity = a;
m_temperature = b;
m_pressure = c;
notifyOb();
}
void registerob(ObserverBoardInterface* ob)
{
m_obs.push_back(ob);
}
void removeob(ObserverBoardInterface* ob)
{
m_obs.remove(ob);
}
protected:
void notifyOb()
{
list<ObserverBoardInterface*>::iterator pos = m_obs.begin();
while (pos != m_obs.end())
{
((ObserverBoardInterface* )(*pos))->update(m_humidity,m_temperature,m_pressure);
(dynamic_cast<DisplayBoardInterface*>(*pos))->show();
++pos;
}
}
private:
float m_humidity;
float m_temperature;
float m_pressure;
list<ObserverBoardInterface* > m_obs;
};
// A Concrete Observer
class CurrentConditionBoard : public ObserverBoardInterface, public DisplayBoardInterface
{
public:
CurrentConditionBoard(WeatherDataInterface& a):m_data(a)
{
m_data.registerob(this);
}
void show()
{
cout<<"_____CurrentConditionBoard_____"<<endl;
cout<<"humidity: "<<m_h<<endl;
cout<<"temperature: "<<m_t<<endl;
cout<<"pressure: "<<m_p<<endl;
cout<<"_______________________________"<<endl;
}
void update(float h, float t, float p)
{
m_h = h;
m_t = t;
m_p = p;
}
private:
float m_h;
float m_t;
float m_p;
WeatherDataInterface& m_data;
};
// A Concrete Observer
class StatisticBoard : public ObserverBoardInterface, public DisplayBoardInterface
{
public:
StatisticBoard(WeatherDataInterface& a):m_maxt(-1000),m_mint(1000),m_avet(0),m_count(0),m_data(a)
{
m_data.registerob(this);
}
void show()
{
cout<<"________StatisticBoard_________"<<endl;
cout<<"lowest temperature: "<<m_mint<<endl;
cout<<"highest temperature: "<<m_maxt<<endl;
cout<<"average temperature: "<<m_avet<<endl;
cout<<"_______________________________"<<endl;
}
void update(float h, float t, float p)
{
++m_count;
if (t>m_maxt)
{
m_maxt = t;
}
if (t<m_mint)
{
m_mint = t;
}
m_avet = (m_avet * (m_count-1) + t)/m_count;
}
private:
float m_maxt;
float m_mint;
float m_avet;
int m_count;
WeatherDataInterface& m_data;
};
int main(int argc, char *argv[])
{
ParaWeatherData * wdata = new ParaWeatherData;
CurrentConditionBoard* currentB = new CurrentConditionBoard(*wdata);
StatisticBoard* statisticB = new StatisticBoard(*wdata);
wdata->SensorDataChange(10.2, 28.2, 1001);
wdata->SensorDataChange(12, 30.12, 1003);
wdata->SensorDataChange(10.2, 26, 806);
wdata->SensorDataChange(10.3, 35.9, 900);
wdata->removeob(currentB);
wdata->SensorDataChange(100, 40, 1900);
delete statisticB;
delete currentB;
delete wdata;
return 0;
}
摘自 ASCE1885