观察者模式(Observer Pattern)主要解决的是当对象间存在一对多关系时当一个对象被修改,会自动通知依赖它的其它对象。在设计模式中观察者模式属于行为型模式。
1 经典观察者模式
设计模式相关的书籍中,介绍观察者模式时大都用下面的图:
如上,上面的类图就是一个简单的观察者模式,上面的类说明如下:
实现上面这个观察者模式的代码如下:
class Observer{
public:
virtual void Update(int) = 0;
};
class Subject{
public:
virtual void Attach(Observer *) = 0;
virtual void Detach(Observer *) = 0;
virtual void Notify() = 0;
};
class ConcreteObserver : public Observer{
public:
ConcreteObserver(Subject *pSubject) : m_pSubject(pSubject){};
void Update(int value){
cout << "接收到信息通知:" << value << endl;
}
private:
Subject *m_pSubject;
};
class ConcreteSubject : public Subject{
public:
void Attach(Observer *pObserver);
void Detach(Observer *pObserver);
void Notify();
void SetState(int state){
m_iState = state;
}
void GetState(){
return m_iState;
}
private:
std::list<Observer *> m_ObserverList;
int m_iState;
};
void ConcreteSubject::Attach(Observer *pObserver){
m_ObserverList.push_back(pObserver);
}
void ConcreteSubject::Detach(Observer *pObserver){
m_ObserverList.remove(pObserver);
}
void ConcreteSubject::Notify(){
std::list<Observer *>::iterator it = m_ObserverList.begin();
while (it != m_ObserverList.end()){
(*it)->Update(m_iState);
++it;
}
}
如上面代码所示,实现了一个经典的观察者模式,但是在实际的使用时,这种实现方式并不灵活,有很多的限定,最明显的两个限制是:
2 改进后的观察者模式
为了解决经典观察者模式中的问题,可以使用C++11中提供的新的语言特性,如将消息通知接口进行参数化和使用std::function函数绑定解决类的继承问题,通过完美转发和可变参数模板消除接口接口变化的影响。经过改进后的观察模式有新增观察者时不需要继承基类。只要新增一个事件类型即可。
在本次代码实现中,我们不希望新增的事件类型能够进行复制,因此,在实现时将会用=default和=delete标识符对类的特殊函数进行限制,改进后的观察者模式代码如下:
class NonCopyable{
protected:
NonCopyable()=default;
~NonCopyable()=default;
NonCopyable(const NonCopyable &)=delete;
NonCopyable & operator =(const NonCopyable &) = delete;
};
template <typename Func>
class Event:NonCopyable{
public:
Event()=default;
~Event()=delete;
//注册观察者,支持右值引用
int Attach(Func && f){
return Assign(f);
}
//注册观察者,支持左值引用
int Attach(Func & f){
return Assign(f);
}
//移除观察者
void Deatach(int iKey){
m_mapOberserve.erase(iKey);
}
//通知接口
template <typename... Args>
void Notify(Args&&... args){
for(auto& it:m_mapOberserve)
{
it.second(std::forward<Args>(args)...);
}
}
private:
template <typename F>
int Assign(F && f){
int k = m_iObersevre++;
m_mapOberserve.emplace(k,std::forward<F>(f));
return k;
}
int m_iObersevre = 0;
std::map<int,Func> m_mapOberserve;
};
如上面的代码所示,使用C++11后的观察者模式通过维护一个泛型函数列表消除了类继承导致的代码耦合,使得只需要传入观察者调用函数既可。通知接口通过可变参数模版也实现了任意参数的输入消除了通知接口不适应业务发展变化的影响。