设计模式-观察者模式 C++
一、简介观察者模式属于行为型模式。意图:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。主要解决:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。二、建立和使用观察者模式1、建立观察者组件首先,我们有一个被观察的对象A,和观察对象A的观察者对象B、C、D。则我们需要1)、声明一个抽象观察者类,类中声明观察者响应
一、简介
观察者模式属于行为型模式。
意图:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
主要解决:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。
二、建立和使用观察者模式
1、建立观察者组件
首先,我们有一个被观察的对象A,和观察对象A的观察者对象B、C、D。则我们需要
1)、声明一个抽象观察者类,类中声明观察者响应函数;
2)、被观察对象A需要一个成员数据,观察者对象的指针列表_observerList,用于存放观察者们的指针。并提供在_observerList中添加、移除观察者指针的接口,即在_observerList中进行增删的方法。同时还可能需要要定义被观察事件的触发函数;
3)、观察者对象B、C、D需要继承一抽象观察者类,并实现响应函数;
2、使用观察者组件
1)首先,在B、C、D的对象中,要先获取到A的指针,并调用A的添加观察者指针接口,把this指针保存进A的_observerList中;
2)然后,当A中被关注的事件发生时,A根据_observerList存储的指针依次调用响应函数;
3)B、C、D分别对观察的事件进行响应;
三、观察者模式使用实例
假设数学教师A预计明天进行一场数学考试,同时体育老师,学生A,学生B和关注了数学考试的事件。如果将有数学考试发生时,体育老师,学生A,学生B都会收到通知。
1、首先,我们根据目前情况,先创建观察者组件
1)创建观察者抽象类EventObserver
class EventObserver
{
public:
virtual ~EventObserver() {}
virtual void MathExamEvent(bool isMorning) = 0;
};
其中MathExamEvent就是定义的响应函数,此函数可以声明为纯虚函数也可以声明为虚函数,如果EventObserver有子类并不需要观察MathExamEvent事件,同时也不打算实现MathExamEvent,则可以声明为虚函数。
2)创建被观察对象。此处我们创建数学老师类
class MathTeacher
{
public:
//添加观察者
void addObserver(EventObserver* ob)
{
_mtx.lock();
//排重处理
if (_observerList.end() == std::find(_observerList.begin(), _observerList.end(), ob))
{
_observerList.push_back(ob);
}
_mtx.unlock();
}
//移除观察者
void RemoveObserver(EventObserver* ob)
{
_mtx.lock();
vector<EventObserver*>::iterator it = std::find(_observerList.begin(), _observerList.end(), ob);
if (_observerList.end() != it)
{
_observerList.erase(it);
}
_mtx.unlock();
}
//数学考试
void MathExam(bool isMorning)
{
cout << "明天将有一场数学考试!" << endl;
//通知各个观察者这场考试
_mtx.lock();
for (auto it : _observerList)
{
it->MathExamEvent(isMorning);
}
_mtx.unlock();
}
MathTeacher() : _observerList(), _mtx() {}
private:
vector<EventObserver*> _observerList;
std::mutex _mtx; //如果是多线程编程,需要对_observerList加锁保护
};
结合上诉代码,我们可以对应到被观察类的基本组件如下:
观察者对象的指针列表:_observerList
添加、移除观察者指针的接口:addObserver、RemoveObserver
被观察事件的触发函数:MathExam
3)创建观察者实现类,需要继承观察者抽象类EventObserver,实现对MathExamEvent的响应。此处,我们声明体育老师、学生A、B三个类
//体育老师类
class PETeacher : public EventObserver
{
virtual void MathExamEvent(bool isMorning)
{
cout << "我是体育老师,我了解到明天将有一场数学考试。" << endl;
}
};
//学生A类
class StudentA: public EventObserver
{
public:
StudentA() {}
StudentA(MathTeacher& mathTeacher)
{
mathTeacher.addObserver(this);
}
virtual void MathExamEvent(bool isMorning)
{
cout << "我是小A:";
Study();
}
private:
void Study()
{
cout << "开始认真学习!" << endl;
}
};
//学生B类
class StudentB : public EventObserver
{
public:
StudentB() {}
StudentB(MathTeacher& mathTeacher)
{
mathTeacher.addObserver(this);
}
virtual void MathExamEvent(bool isMorning)
{
cout << "我是小B:";
if (!isMorning)
{
Play();
}
Study();
}
private:
void Study()
{
cout << "开始认真学习!" << endl;
}
void Play()
{
cout << "开始玩耍!" << endl;
}
};
可见,三个类都会数学考试事件MathExamEvent做出了实现,其中体育老师并没有做出响应,两位学生对这场考试分别有自己的对策。学生A打算立刻学习,准备考试;学生B认为如果考试在下午,可以先玩耍一会儿。
【注意】
一般情况下,我们要避免各观察者类在观察事件响应函数中做太多的事情,已防止阻塞对其他观察者响应函数的调用和触发函数的继续运行。如果某一观察者类的响应确实十分繁琐,必要情况下我们可以抛线程执行。
比如,以上诉考试事件为例,因为“数学老师”是依次通知各观察者的,假如通知“体育老师”的时候,体育老师为了详细了解这场考试的情况,和“数学老师”聊了大半天并当场做出一大堆计划,则数学老师资源无法释放,会妨碍学生A,B得到这场考试的通知,同时数学老师也无法做其他事情。此时,体育老师可以先停止和数学老师的沟通,释放数学老师后,自己做出处理。见下列代码
体育老师需要做出计划
//体育老师类
class PETeacher : public EventObserver
{
virtual void MathExamEvent(bool isMorning)
{
cout << "我是体育老师,我了解到明天将有一场数学考试。" << endl;
DoPlan();
}
private:
void DoPlan()
{
cout << "体育老师:结合相关信息,做出上课计划!" << endl;
int plan = 100;
for (int i = 0; i < 100; i++)
{
cout << "体育老师:计划" << i << "开始" << endl;
}
}
};
由于计划十分繁琐,我们可以抛线程执行,不阻塞主线程
//体育老师类
class PETeacher : public EventObserver
{
public:
virtual void MathExamEvent(bool isMorning)
{
cout << "我是体育老师,我了解到明天将有一场数学考试。" << endl;
//抛线程执行DoPlan
threadObj = new thread(PETeacher::DoPlan);
}
static void DoPlan()
{
cout << "体育老师:结合相关信息,做出上课计划!" << endl;
int plan = 100;
for (int i = 0; i < 100; i++)
{
cout << "体育老师:计划" << i << "开始" << endl;
}
}
PETeacher() : threadObj(nullptr) {}
~PETeacher()
{
if (nullptr != threadObj)
{
delete threadObj;
threadObj = nullptr;
}
}
private:
std::thread* threadObj;
};
2、观察者创建完毕,接下来展示的是对观察者的使用代码
int main()
{
//创建实例
MathTeacher mathTeacher;
PETeacher peTeacher;
//体育老师构造后,调用数学老师的addObserver方法添加观察者
mathTeacher.addObserver(&peTeacher);
//构造函数中添加观察者
StudentA stdA(mathTeacher);
StudentB stdB(mathTeacher);
//数学老师计划组织数学考试
mathTeacher.MathExam(true);
system("pause");
return 0;
}
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)