c/c++设计模式---观察者模式
namespace _nmsp1 { class Fighter; //类前向声明 list<Fighter*> g_playerList; //玩家父类(以往的战斗者类) class Fighter { public: Fighter(int tmpID, string tmpName) :m_iPlayerID(tmpID), m_sPlayerName(tmpName) //构造函数 { m_iFamilyID = -1; //-1表示没有加入任何家族 } virtual ~Fighter() {} //析构函数 public: void SetFamilyID(int tmpID) //加入家族的时候要设置家族ID { m_iFamilyID = tmpID; } public: void SayWords(string tmpContent) //玩家说了某句话 { if (m_iFamilyID != -1) { //该玩家属于某个家族,应该把聊天内容信息传送给该家族的其他玩家 for (auto iter = g_playerList.begin(); iter != g_playerList.end(); ++iter) { if (m_iFamilyID == (*iter)->m_iFamilyID) { //同一个家族的其他玩家也应该收到聊天信息 NotifyWords((*iter), tmpContent); } } } } private: void NotifyWords(Fighter* otherPlayer, string tmpContent) //其他玩家收到了当前玩家的聊天信息 { //显示信息 cout << "玩家:" << otherPlayer->m_sPlayerName << "收到了玩家:" << m_sPlayerName << " 发送的聊天信息:" << tmpContent << endl; } private: int m_iPlayerID; //玩家ID,全局唯一 string m_sPlayerName; //玩家名字 int m_iFamilyID; //家族ID }; //"战士"类玩家,父类为Fighter class F_Warrior :public Fighter { public: F_Warrior(int tmpID, string tmpName) :Fighter(tmpID, tmpName) {} //构造函数 }; //"法师"类玩家,父类为Fighter class F_Mage :public Fighter { public: F_Mage(int tmpID, string tmpName) :Fighter(tmpID, tmpName) {} //构造函数 }; } int main() { //创建游戏玩家 _nmsp1::Fighter* pplayerobj1 = new _nmsp1::F_Warrior(10, "张三"); //实际游戏中很多数据取自数据库。 pplayerobj1->SetFamilyID(100); //假设该玩家所在的家族的家族ID是100 _nmsp1::g_playerList.push_back(pplayerobj1); //加入到全局玩家列表中 _nmsp1::Fighter* pplayerobj2 = new _nmsp1::F_Warrior(20, "李四"); pplayerobj2->SetFamilyID(100); _nmsp1::g_playerList.push_back(pplayerobj2); _nmsp1::Fighter* pplayerobj3 = new _nmsp1::F_Mage(30, "王五"); pplayerobj3->SetFamilyID(100); _nmsp1::g_playerList.push_back(pplayerobj3); _nmsp1::Fighter* pplayerobj4 = new _nmsp1::F_Mage(50, "赵六"); pplayerobj4->SetFamilyID(200); //赵六和前面三人属于两个不同的家族 _nmsp1::g_playerList.push_back(pplayerobj4); //当某个玩家聊天时,同族人都应该收到该信息 pplayerobj1->SayWords("全族人立即到沼泽地集结,准备进攻!"); //释放资源 delete pplayerobj1; delete pplayerobj2; delete pplayerobj3; delete pplayerobj4; }
namespace _nmsp2 { class Fighter; //类前向声明 class Notifier //通知器父类 { public: virtual void addToList(Fighter* player) = 0; //把要被通知的玩家加入到列表中 virtual void removeFromList(Fighter* player) = 0; //把不想被通知的玩家从列表中去除 virtual void notify(Fighter* talker, string tmpContent) = 0; //通知的一些细节信息 virtual ~Notifier() {} }; //玩家父类 class Fighter { public: Fighter(int tmpID, string tmpName) :m_iPlayerID(tmpID), m_sPlayerName(tmpName) //构造函数 { m_iFamilyID = -1; //-1表示没有加入任何家族 } virtual ~Fighter() {} //析构函数 public: void SetFamilyID(int tmpID) //加入家族的时候要设置家族ID { m_iFamilyID = tmpID; } int GetFamilyID() //获取家族ID { return m_iFamilyID; } public: void SayWords(string tmpContent,Notifier *notifier) //玩家说了某句话 { notifier->notify(this, tmpContent); } //通知该玩家接收到其他玩家发送来的聊天信息,虚函数,子类可以覆盖以实现不同的功能 virtual void NotifyWords(Fighter* talker, string tmpContent) { //显示信息 cout << "玩家:" << m_sPlayerName << "收到了玩家:" << talker->m_sPlayerName << " 发送的聊天信息:" << tmpContent << endl; } private: int m_iPlayerID; //玩家ID,全局唯一 string m_sPlayerName; //玩家名字 int m_iFamilyID; //家族ID }; //"战士"类玩家,父类为Fighter class F_Warrior :public Fighter { public: F_Warrior(int tmpID, string tmpName) :Fighter(tmpID, tmpName) {} //构造函数 }; //"法师"类玩家,父类为Fighter class F_Mage :public Fighter { public: F_Mage(int tmpID, string tmpName) :Fighter(tmpID, tmpName) {} //构造函数 }; //聊天信息通知器 class TalkNotifier :public Notifier { public: //将玩家增加到家族列表中来 virtual void addToList(Fighter* player) { int tmpfamilyid = player->GetFamilyID(); if (tmpfamilyid != -1) //加入了某个家族 { auto iter = m_familyList.find(tmpfamilyid); if (iter != m_familyList.end()) { //该家族id在map中已经存在 iter->second.push_back(player); //直接把该玩家加入到该家族 } else { //该家族id在map中不存在 list<Fighter*> tmpplayerlist; m_familyList.insert(make_pair(tmpfamilyid, tmpplayerlist)); //以该家族id为key,增加条目到map中 m_familyList[tmpfamilyid].push_back(player); //向该家族中增加第一个玩家 } } } //将玩家从家族列表中删除 virtual void removeFromList(Fighter* player) { int tmpfamilyid = player->GetFamilyID(); if (tmpfamilyid != -1) //加入了某个家族 { auto iter = m_familyList.find(tmpfamilyid); if (iter != m_familyList.end()) { m_familyList[tmpfamilyid].remove(player); } } } //家族中某玩家说了句话,调用该函数来通知家族中所有人 virtual void notify(Fighter* talker, string tmpContent) //talker是讲话的玩家 { int tmpfamilyid = talker->GetFamilyID(); if (tmpfamilyid != -1) //加入了某个家族 { auto itermap = m_familyList.find(tmpfamilyid); if (itermap != m_familyList.end()) { //遍历该玩家所属家族的所有成员 for (auto iterlist = itermap->second.begin(); iterlist != itermap->second.end(); ++iterlist) { (*iterlist)->NotifyWords(talker, tmpContent); } } } } private: //map中的key表示家族id,value代表该家族中所有玩家列表 map<int, list<Fighter*> > m_familyList; }; } int main() { _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);//程序退出时检测内存泄漏并显示到“输出”窗口 //创建游戏玩家 _nmsp2::Fighter* pplayerobj1 = new _nmsp2::F_Warrior(10, "张三"); //实际游戏中很多数据取自数据库。 pplayerobj1->SetFamilyID(100); //假设该玩家所在的家族的家族ID是100 _nmsp2::Fighter* pplayerobj2 = new _nmsp2::F_Warrior(20, "李四"); pplayerobj2->SetFamilyID(100); _nmsp2::Fighter* pplayerobj3 = new _nmsp2::F_Mage(30, "王五"); pplayerobj3->SetFamilyID(100); _nmsp2::Fighter* pplayerobj4 = new _nmsp2::F_Mage(50, "赵六"); pplayerobj4->SetFamilyID(200); //赵六和前面三人属于两个不同的家族 //创建通知器 _nmsp2::Notifier* ptalknotify = new _nmsp2::TalkNotifier(); //玩家增加到家族列表中来,这样才能收到家族聊天信息 ptalknotify->addToList(pplayerobj1); ptalknotify->addToList(pplayerobj2); ptalknotify->addToList(pplayerobj3); ptalknotify->addToList(pplayerobj4); //某游戏玩家聊天,同族人都应该收到该信息 pplayerobj1->SayWords("全族人立即到沼泽地集结,准备进攻!", ptalknotify); cout << "王五不想再收到家族其他成员的聊天信息了---" << endl; ptalknotify->removeFromList(pplayerobj3); //将王五从家族列表中删除 pplayerobj2->SayWords("请大家听从族长调遣,前往沼泽地!", ptalknotify); //释放资源 delete pplayerobj1; delete pplayerobj2; delete pplayerobj3; delete pplayerobj4; delete ptalknotify; return 0; }
观察者(Observer)模式
//(1)一个遍历问题导致的低效率范例
//(2)引入观察者(Observer)模式
//观察者设计模式 定义(实现意图):定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于
//它的对象都会自动得到通知。
//发布-订阅(Publish-Subscribe);
//观察者模式的四种角色
//a)Subject(主题):观察目标,这里指Notifier类。
//b)ConcreteSubject(具体主题):这里指TalkNotifier类。
//c)Observer(观察者):这里指Fighter类。
//d)ConcreteObserver(具体观察者):这里指F_Warrior和F_Mage子类。
//观察者模式的特点:
//a)在观察者和观察目标之间建立了一个抽象的耦合
//b)观察目标会向观察者列表中的所有观察者发送通知。
//c)可以通过增加代码来增加新的观察者或者观察目标,符合开闭原则
//(3)应用联想
//a)救援家族成员镖车
//b)将新闻推荐给符合其胃口的读者
//c)通过改变自身绘制的图形来真实的反应公司的销售数据。
//d)炮楼只会对30米内的玩家(列表内玩家)进行攻击。
观察者模式是一种行为型设计模式,它定义了一种一对多的依赖关系,使得多个观察者对象能够监听某一个主题对象。当该主题对象状态发生变化时,它会通知所有观察者对象,使得它们能够自动更新自己。这个模式非常适合用于事件处理系统,例如GUI框架、通知系统等。
在C++中实现观察者模式通常涉及三个主要组件:
- 主题(Subject):维护一个观察者列表,提供注册和移除观察者的方法,以及通知所有观察者的方法。
- 观察者(Observer):定义一个接口或抽象类,以便接收通知。
- 具体主题(Concrete Subject) 和 具体观察者(Concrete Observer):具体的实现类。
下面是一个简单的C++实现观察者模式的例子:
1. 定义观察者接口
首先,定义一个纯虚类 Observer
,作为所有观察者的基类:
cppCopy Code
// Observer.h
#ifndef OBSERVER_H
#define OBSERVER_H
class Observer {
public:
virtual ~Observer() {}
virtual void update(int value) = 0; // 纯虚函数,用于接收更新通知
};
#endif // OBSERVER_H
2. 定义主题类
然后,定义一个 Subject
类,包含注册、移除和通知观察者的方法:
cppCopy Code
// Subject.h
#ifndef SUBJECT_H
#define SUBJECT_H
#include <vector>
#include "Observer.h"
class Subject {
public:
void attach(Observer* observer) {
observers.push_back(observer);
}
void detach(Observer* observer) {
observers.erase(std::remove(observers.begin(), observers.end(), observer), observers.end());
}
void notify() {
for (Observer* observer : observers) {
observer->update(value);
}
}
void setValue(int newValue) {
value = newValue;
notify();
}
private:
std::vector<Observer*> observers;
int value;
};
#endif // SUBJECT_H
3. 实现具体观察者
创建具体的观察者类,实现 Observer
接口:
cppCopy Code
// ConcreteObserver.h
#ifndef CONCRETE_OBSERVER_H
#define CONCRETE_OBSERVER_H
#include <iostream>
#include "Observer.h"
class ConcreteObserver : public Observer {
public:
void update(int value) override {
std::cout << "ConcreteObserver: Value updated to " << value << std::endl;
}
};
#endif // CONCRETE_OBSERVER_H
4. 使用观察者模式
最后,在主程序中创建主题和观察者,并进行交互:
cppCopy Code
// main.cpp
#include <iostream>
#include "Subject.h"
#include "ConcreteObserver.h"
int main() {
Subject subject;
ConcreteObserver observer1;
ConcreteObserver observer2;
subject.attach(&observer1);
subject.attach(&observer2);
subject.setValue(10); // 通知所有观察者,值更新为10
subject.setValue(20); // 通知所有观察者,值更新为20
subject.detach(&observer1);
subject.setValue(30); // 通知剩余的观察者,值更新为30
return 0;
}
总结
以上代码展示了如何在C++中实现观察者模式。这个简单的实现可以根据实际需求进行扩展,比如添加更多的观察者类型、处理复杂的状态更新逻辑等。通过使用观察者模式,可以有效地实现对象之间的一对多依赖关系,从而实现松耦合的设计,提高代码的可维护性和可扩展性。