前言:观察者模式,顾名思义可定要有一个观察者(也叫订阅者),有观察者必要有被观察者,被观察者这里我们叫做主题。一个主题可以有多个订阅者,一个订阅者也可以由多个主题(不属于观察者的内容,不做研究),因为是一对多,所以类似广播通信。煮个栗子说明:报社和订报的人,报社就是主题,订报的人就是观察者,报社出了一期新的报纸,要通知订阅报纸的人,订报的人收到报纸,做出反应(读取还是扔掉)。
那么,问题来了,怎么实现一个对象(主题)做出变化,另一个对象(订阅者)收到消息并且做出改变呢?那么两者肯定有耦合关系。观察者模式是怎么实现的呢?在观察这模式中,主题类中添加一个观察者类的链表,通过成员函数通知观察者,观察者收到消息,做出反应。是不是还是感到一脸懵逼,没关系,往下看代码实现。
注意:此项设计模式设计到一项概念就是链表,没有list知识的,也不碍事,现学现卖亦可。代买中有详细解释,相信你能看懂。
obs.h代码如下:
#ifndef _OBS_H
#define _OBS_H
#include<string>
#include<list>
#include<iostream>
using namespace std;
class NEWS;//因Observe中有NEWS对象,所以要先声明。
class Observe//声明一个抽象观察者类;
{
public:
string name;//定义观察者名单,声明为公用的是因为在主题NEWS中输出了观察者名单。
NEWS* news;//定义了主题对象
public:
Observe();//默认构造函数
~Observe();//析构函数
Observe(string n_name, NEWS*n_news):name(n_name), news(n_news)//见注1。
{
}
virtual void update() = 0;//声明纯虚函数。见注2。
};
Observe::Observe() {}//默认构造函数
Observe::~Observe() {}//析构函数
class obsA :public Observe//定义具体观察者obsA
{
public:
obsA();
~obsA();
obsA(string name, NEWS*news) :Observe(name, news)
{
}
void update();//观察者obsA收到消息更新自己的动作。
};
class obsB :public Observe
{
public:
obsB();
~obsB();
obsB(string name, NEWS*news) :Observe(name, news)
{
}
void update();//观察者obsB收到消息更新自己的动作。
};
class obsC :public Observe
{
public:
obsC();
~obsC();
obsC(string name, NEWS*news) :Observe(name, news)
{
}
void update();//观察者obsC收到消息更新自己的动作。
};
class NEWS//定义抽象基类新闻类
{
protected:
list<Observe*>Obs;//观察者模式的核心--在一个对象中存在另一个对象的链表
public:
string act;
virtual void add(Observe*) = 0;
virtual void Delete(Observe*) = 0;
virtual void notify() = 0;
virtual void Show() = 0;
};
class newsA :public NEWS//抽象新闻类实例化
{
public:
void add(Observe*obs)//添加订阅者
{
Obs.push_back(obs);
}
void Delete(Observe*obs)//删除订阅者
{
list<Observe*>::iterator it;
for (it = Obs.begin(); it != Obs.end(); )//见注3。
{
if ((*it) == obs)
{
it=Obs.erase(it);
}
else
{
it++;
}
}
}
void Show()
{
list<Observe*>::iterator it;
cout << "当前订阅人员名单:" << endl;
for (it = Obs.begin(); it != Obs.end(); it++)//注意begin和end后面的括号不要忘记了
{
cout <<(*it)->name << endl;//公有化观察者对象成员name。
}
}
void notify()
{
list<Observe*>::iterator it;
for (it = Obs.begin(); it != Obs.end(); it++)
{
(*it)->update();//观察者模式的另一个核心--通知所有观察者对象的函数
}
}
};
obsA::obsA(){}
obsA::~obsA() {}
void obsA::update()
{
cout << name << " 收到消息:" << news->act << endl;
cout << "我不想看,扔掉" << endl;
}
obsB::obsB() {}
obsB::~obsB() {}
void obsB::update()
{
cout << name << " 收到消息:" << news->act << endl;
cout << "放在桌子上,一会去看" << endl;
}
obsC::obsC() {}
obsC::~obsC() {}
void obsC::update()
{
cout << name << " 收到消息:" << news->act << endl;
cout << "不想看China Daily的报纸了,打电话告诉出版社取消订阅" << endl;
}
#endif
主程序实现如下:
#include "obs.h"
#include <stdlib.h>
int main()
{
NEWS *ChinaDaily = new newsA();//见注4。
Observe *xiaoming = new obsA("xiaoming", ChinaDaily);
Observe *xiaowang = new obsB("xiaowang", ChinaDaily);
Observe *xiaozhang = new obsC("xiaozhang", ChinaDaily);
ChinaDaily->add(xiaoming);//添加订阅者名单
ChinaDaily->add(xiaowang);
ChinaDaily->add(xiaozhang);
ChinaDaily->Show();//输出订阅者名单
cout << endl;
ChinaDaily->act = "ChinaDaily有新报纸派送啦";//报社做出反应
ChinaDaily->notify();//通知订阅者
cout << endl;
ChinaDaily->Delete(xiaozhang);
ChinaDaily->Show();
cout << endl;
return 0;
}
运行结果:
注1:
此乃列表成员初始化变量。还有一种成员初始化变量看起来很玄乎。
Observe(string name, NEWS*news)
{
this->name = name;
this->news = news;
}
第一次看见的时候,不知道你懵不懵,反正我懵了。this什么鬼?仔细一看,给变量赋值的时候,变量名字起的一样,如果不加this,直接写上name =name 我感觉会更懵。完全可以把变量名字换成另外一个,但是使用this看起来更加简洁,更加美观,做成了变量的统一,看个人喜好吧。
注2:
虚函数为零为纯虚函数,这是抽象类的标志。大家知道抽象基类不能实例化,但是为什么要定义抽象类呢?不定义抽象基类照样可以实现功能。查阅了资料,以下来自百度。
四个字:制定标准
纯虚函数是为你的程序制定一种标准,即只要你继承了我,就必须按照我和标准来,实现我所有的方法,否则你也是虚拟的,和JAVA里的接口一样,都是制定标准,为了使程序更加通用化,可重用性提高,让所有实现它或继承自它的子类全部按同一标准来工作,你想想大家都按标准来对程序的益处是什么?
注3:
删除list成员。或许你很奇怪为什么会这样写,并且for循环里面就两个参数。原因是当list遍历成员并且想要用erase删除的时候,erase迭代器之后,原来的迭代器就失效了,程序编译没有问题,但是执行的时候就会出现错误。erase会返回一个指向list下一个元素的iter,如果想要遍历就需要用返回的iter继续操作,如果不想遍历,直接加上一条break亦可。
注4:
这里是定义了一个指向子类对象的父类指针。这是面向对象编程的特性--多态。父类指针指向子类,则此指针只能访问父类的成员函数,但是这样就无法实现多态,我想要实现多态怎么办呢?只需要在成员函数前面加上virtual就可以了。
总结:观察者模式的核心就是,主题(被观察者)中有一个订阅者(观察者)链表,当主题发生变化的时候,执行主题中notify函数,循环链表,执行观察者中的update函数。