大家应该都看过报纸吧,没看过?那最起码应该听说过吧。好的,那接下来我们分析一下我们的报纸是怎么得到的?有人会说印出来的,我当然知道是印出来的,不打印难道天上掉下来的?
为了每天得到报纸,我们则需要向报社订阅,我们只有订阅了,报社才会在以后每天定时派送报纸给我们。
接下来我们分析一下,我们订阅的流程:
- 首先我们需要给报社打电话说,我需要订阅你们的报纸。
- 报社接到你的电话后便会询问你的住址并将你的信息以及住址记录下来,方便每天配送报纸使用。
- 接下来每天早上我们就会收到最新的报纸。
然后忽然间有一天我们发现,好像电子邮件更方便,然后我们通知报社,以后我再不需要报纸了,报社收到此消息后便会把你的信息从他们的记录中抹除,好的,接下来我们便不会再次收到报纸了。
咦,有人会问,我们今天要学的是观察者模式,你给我扯如何订报纸干嘛?
大家不要急嘛,通过定报纸这件事情我们发现,如果我们需要报纸那么便向报社订阅,当我们再需要报纸的时候取消订阅就好了。
...
好好好,不说报纸了,那我们进入主题,首先我们看下观察者模式的定义:
观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会受到通知并自动更新。
我们来分析一下,一对多,观察者,被观察者?联想一下上面我所说的订阅报纸的案例,没关系?怎么可能,你看,这个“一对多”种的“一”像不像报社,“多”又像不像我们所有订阅报纸的用户,当报社有了新报纸之后便会将报纸推送到所有人的手里。只要报社不关门,订阅者便能每天都收到报社最新的报纸。
那我们可不可以把这种形式应用到我们写程序上面呢?
首先我们需要一个报社,也就是被观察者以及多个订阅者,也就是观察者,按照上面我们订报纸的流程,首先我们所有的观察者需要向被观察者注册,类似于订阅报纸,而我们的被观察者则需要保留所有观察者的地址,方便推送更新数据,也就是我们需要在报社留存我们的家庭地址,接下来便是更新数据的推送。
注意:在这里我们的观察者与被观察者之前是松耦合的,虽然观察者知道被观察者,被观察者也知道观察者,但是观察者并不知道被观察者内部数据是怎么更新的,被观察者也不知道观察者得到数据之后会怎么处理。
好了接下来我们看一个例子:
#include <vector>
#include <algorithm>
#include <functional>
#include <iostream>
using namespace std;
class Subject;
class ObserverBase
{
public:
ObserverBase(Subject *s)
{
m_subject = s;
nSubject = 0;
}
void update(int n) {//接收更新数据的函数
nSubject = n;
display();
}
virtual void display() = 0;//所有子类必须实现的函数,也就是当接收到新数据时该类应该如何处理。
protected:
int nSubject;
Subject *m_subject;//这里保留主题的指针是为了取消订阅使用
};
class Subject
{
public:
Subject() {
m_nSubject = 0;
m_bChanged = false;
}
void registerObserver(ObserverBase *o) //注册函数
{
m_observers.push_back(o);
}
void removeObserver(ObserverBase *o) //取消注册
{
m_observers.erase(remove_if(m_observers.begin(), m_observers.end(), bind(equal_to<ObserverBase *>(),placeholders::_1,o)), m_observers.end());
}
void notifyObservers() //通知所有用户有新消息
{
if (m_bChanged)
{
for_each(m_observers.begin(), m_observers.end(), [=](ObserverBase *o) { o->update(m_nSubject); });
}
m_bChanged = false;
}
void changeSubject(int n)
{
m_nSubject = n;
//....
setChanged(true);
notifyObservers();
}
void setChanged(bool b)
{
m_bChanged = b;
}
bool getChanged() const
{
return m_bChanged;
}
private:
vector<ObserverBase*>m_observers;
int m_nSubject;
/*可能有些人不明白这个参数的作用,我们报社是在搜集到一整篇报纸后才会推送给用户,而不是有一条
消息便推送给用户,这个参数就是为了防止推送的太频繁。*/
bool m_bChanged;
};
class ObserverA:public ObserverBase
{
public:
ObserverA(Subject*s) :ObserverBase(s) {
}
void display() {
cout << "ObserverA主题更改为:" << nSubject<<endl;
}
};
class ObserverB :public ObserverBase
{
public:
ObserverB(Subject*s) :ObserverBase(s) {
}
void display() {
cout << "ObserverB主题更改为:" << nSubject<<endl;
if (nSubject >= 1)//当接收到的数据大于等于1之后,该类则取消注册。
{
m_subject->removeObserver(this);
}
}
};
int main()
{
Subject *sub = new Subject;
ObserverA *a = new ObserverA(sub);
ObserverB *b = new ObserverB(sub);
sub->registerObserver(a);
sub->registerObserver(b);
for (int i=0;i<5;++i)
{
sub->changeSubject(i);
}
return 0;
}
输出结果:
ObserverA主题更改为:0
ObserverB主题更改为:0
ObserverA主题更改为:0
ObserverB主题更改为:1
ObserverA主题更改为:1
ObserverA主题更改为:2
ObserverA主题更改为:3
ObserverA主题更改为:4
设计原则:为交互对象之间的松耦合设计而努力。
注:其实两天前就想总结这篇设计模式,但是这几天工作实在是太忙了,没时间去整理,还好今天总算将任务完成了,开心(*^▽^*)。