定义
观察者模式-在对象间定义一对多的依赖关系,这样一来当一个对象的状态更新时,其它对象都能得到通知更新。主题并不需要了解观察者具体实现细节,只需要保证观察者实现了观察者接口即可。
- 观察者模式定义了对象间一对多的关系。即一个主题,多个观察者观察主题的状态变化。
- 主题通过某个公共的接口来通知观察者。
- 不要依赖于特定的通知顺序。除非设计一个能明确通知顺序的主题。
- 一般来说主题对观察者通知是串行的,因此尽量不要在观察者的通知接口中作出繁重的工作
类图
在这里定义了主题的接口ISubject,拥有注册的attach接口和取消注册的detach接口。还有个protected的notify接口通知所有观察者。观察者通过on_notify接口得到通知。
主题实现的是CRoomTemperature类,代表室内温度。有个set_temperature接口设定温度然后触发通知观察者。观察者实现的是CPannel面板,通过on_notify接口得到室温变化,并更新自己的显示。
实现
#include <stdio.h>
#include <string>
#include <map>
#define trace(fmt, ...) printf("[trace] %s:%s:%d " fmt, __FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__)
class IObserver
{
public:
virtual ~IObserver(){};
virtual void on_notify(void *, void *) = 0;
};
class ISubject
{
public:
virtual ~ISubject(){};
virtual int attach(IObserver *, void *) = 0;
virtual int detach(IObserver *) = 0;
protected:
virtual int notify(void *) = 0;
};
class CRoomTemperature: public ISubject
{
public:
int attach(IObserver *observer, void *arg = NULL);
int detach(IObserver *);
int set_temperature(double temp);
protected:
int notify(void *);
private:
typedef std::map<IObserver *, void *> observermap_t;
typedef observermap_t::iterator observermap_itor_t;
typedef std::pair<observermap_itor_t, bool> observermap_ret_t;
typedef std::pair<IObserver *, void *> observermap_pair_t;
observermap_t observermap;
double temp;
};
int CRoomTemperature::attach(IObserver *observer, void *arg)
{
if(observer == NULL)
return -1;
if(observermap.find(observer) != observermap.end()) //already exist
return 0;
observermap_ret_t ret = observermap.insert(observermap_pair_t(observer, arg));
if(ret.second == false)
return -1;
return 0;
}
int CRoomTemperature::detach(IObserver *observer)
{
if(observer == NULL)
return -1;
observermap_itor_t itor = observermap.find(observer);
if(itor == observermap.end()) // not found
return -1;
observermap.erase(itor);
return 0;
}
int CRoomTemperature::set_temperature(double temp)
{
this->temp = temp;
notify((void *)&temp);
return 0;
}
int CRoomTemperature::notify(void *data)
{
observermap_itor_t itor = observermap.begin();
for(; itor != observermap.end(); itor++){
IObserver *observer = (*itor).first;
void *arg = (*itor).second;
observer->on_notify(data, arg); // notify object
}
return 0;
}
class CPanel: public IObserver
{
public:
void on_notify(void *data, void *arg)
{
double temp = *(double *)data;
trace("no notify, temperature: %f\n", temp);
}
};
int main(int argc, char **argv)
{
CRoomTemperature roomtemp;
CPanel panel;
roomtemp.attach(&panel);
roomtemp.set_temperature(18.2);
roomtemp.set_temperature(22.2);
return 0;
}