很多用户的微信号都关注了腾讯官方的微信公众号。当有新的消息时,该公众号就会将信息推送给所有关注者。
这种情况下,用户微信号就是观察者,腾讯官方微信公众号就是被观察者。用户(观察者)有多个,而腾讯官方(被观察者)只有一个。
这就是观察者模式:每个观察者将自己注册给被观察者,当被观察者有新的信息时,就通知给所有的观察者。
cocos2dx的通知功能就使用了观察者模式:当一个类(观察者)需要接受某通知时,就将自己注册给通知管理类(被观察者)。当该通知被发出时,通知管理类就会通知所有对应的观察者,并调用其回调函数。
1. 观察者基类及其派生类定义
观察者可能有多种。所以观察者应该定义一个虚基类,该虚基类定义了观察者的标准接口。然后从该虚基类派生出不同的观察者类。
观察者的标准接口允许接收一个被观察者的指针,从而更新自身的状态。
//预定义被观察者
class Subject;
//观察者
class Observer
{
public:
virtual void Update(Subject* pSubject) = 0;
};
class ConcreteObserverA : public Observer
{
public:
virtual void Update(Subject* pSubject)
{
this->_state = pSubject->GetState();
}
private:
string _state;
};
class ConcreteObserverB : public Observer
{
public:
virtual void Update(Subject* pSubject)
{
this->_state = pSubject->GetState();
}
private:
string _state;
};
2. 被观察者类定义
被观察者需要保存所有的观察者。
同样地,被观察者可能有多种,所以比较好的做法是像观察者那样定义一个虚基类,然后进行不同类型观察者的派生。这里为了简洁,并没有采用这种方式。
//被观察者
class Subject
{
public:
virtual void Notify()
{
list<Observer*>::iterator iter = this->m_lst.begin();
for (; iter != m_lst.end(); iter++)
{
(*iter)->Update(this);
}
};
//添加观察者
virtual void Attach(Observer* pObserver)
{
this->m_lst.push_back(pObserver);
};
//移除观察者
virtual void Detach(Observer* pObserver)
{
list<Observer*>::iterator iter;
iter = find(m_lst.begin(), m_lst.end(), pObserver);
if (iter != m_lst.end())
{
m_lst.erase(iter);
}
};
virtual string GetState() { return this->_state; }
virtual void SetState(string state) { this->_state = state; }
private:
string _state;
list<Observer*> m_lst;
};
3. 用户使用
用户创建出所有观察者对象及一个被观察者对象。将所有观察者注册给被观察者。这样,当被观察者状态更新时,就会向所有观察者发出通知,并调用观察者的回调接口函数。
void main()
{
Observer* p1 = new ConcreteObserverA();
Observer* p2 = new ConcreteObserverB();
Observer* p3 = new ConcreteObserverA();
Subject* pSubject = new Subject();
pSubject->Attach(p1);
pSubject->Attach(p2);
pSubject->Attach(p3);
pSubject->SetState("old");
//发出通知,令所有观察者状态修改为"old"
pSubject->Notify();
pSubject->SetState("new");
//移除观察者p3
pSubject->Detach(p3);
//发出通知,令所有观察者状态修改为"new"
pSubject->Notify();
}
观察者模式在被观察者和观察者之间建立一个抽象的耦合。被观察者角色所知道的只是一个具体现察者聚集,每一个具体现察者都符合一个抽象观察者的接口。被观察者并不认识任何一个具体观察者,它只知道它们都有一个共同的接口。由于被观察者和观察者没有紧密地耦合在一起,因此它们可以属于不同的抽象化层次。
观察者模式支持广播通信。被观察者会向所有的登记过的观察者发出通知。
于被观察者而言,它需要改变所有的观察者,但它并不能预知每次有多少观察者被改变。