当前位置:网站首页>05 观察者(Observer)模式
05 观察者(Observer)模式
2022-06-27 07:47:00 【zzyzxb】
一:一个遍历问题导致的低效率范例
#ifdef _DEBUG //只在Debug(调试)模式下
#ifndef DEBUG_NEW
#define DEBUG_NEW new(_NORMAL_BLOCK,__FILE__,__LINE__) //重新定义new运算符
#define new DEBUG_NEW
#endif
#endif
//#include <boost/type_index.hpp>
using namespace std;
//#pragma warning(disable : 4996)
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;
std::cout << "主线程执行完毕\n";
}
二:引入观察者(Observer)模式
使用观察者设计模式代码如下
#ifdef _DEBUG //只在Debug(调试)模式下
#ifndef DEBUG_NEW
#define DEBUG_NEW new(_NORMAL_BLOCK,__FILE__,__LINE__) //重新定义new运算符
#define new DEBUG_NEW
#endif
#endif
//#include <boost/type_index.hpp>
using namespace std;
//#pragma warning(disable : 4996)
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) {} //构造函数
};
}
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()
{
//创建游戏玩家
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;
std::cout << "主线程执行完毕\n";
}
<1>pplayerobj1->SayWords(“全族人立即到沼泽地集结,准备进攻!”,ptalknotify);
<2>notifier->notify(this, tmpContent);
<3>int tmpfamilyid = talker->GetFamilyID();
<4>auto itermap = m_familyList.find(tmpfamilyid);
<5>for (auto iterlist = itermap->second.begin(); iterlist != itermap->second.end(); ++iterlist) //遍历list容器
(*iterlist)->NotifyWords(talker,tmpContent);
<6>cout << “玩家:” << m_sPlayerName << " 收到了玩家:" << talker->m_sPlayerName << " 发送的聊天信息:" << tmpContent << endl;
观察者设计模式定义(实现意图):定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会自动得到通知。
发布-订阅(Publish-Subscribe)

观察者模式UML图

未抽象通知器情形下的观察者模式UML图
观察者模式的四种角色
a)Subject(主题):观察目标,这里指Notifier类。
b)ConcreteSubject(具体主题):这里指TalkNotifier类。
c)Observer(观察者):这里指Fighter类。
d)ConcreteObserver(具体观察者):这里指F_Warrior和F_Mage子类。

观察者模式角色关系图
观察者模式的特点
a)在观察者和观察目标之间建立了一个抽象的耦合。
b)观察目标会向观察者列表中的所有观察者发送通知。
c)可以通过增加代码来增加新的观察者或者观察目标,符合开闭原则。
三:应用联想
a)救援家族成员镖车。
b)将新闻推荐给符合其胃口的读者。
c)通过改变自身绘制的图形来真实的反应公司的销售数据。
d)炮楼只会对30米内的玩家(列表内玩家)进行攻击。
边栏推荐
- js中输入三个值,并且由小到大输出
- What is a flotation machine?
- RNA SEQ data analysis in R - investigate differentially expressed genes in the data!
- Apifox learning
- Goodbye, agile Scrum
- R language consumption behavior statistics based on association rules and cluster analysis
- win10-如何管理开机启动项?
- JS to determine whether the number entered by the user is a prime number (multiple methods)
- Hutool symmetric encryption
- JS to determine whether the result is qualified, the range is 0-100, otherwise re-enter
猜你喜欢

win命令行中导入、导出数据库相关表

2. QT components used in the project

基础知识 | js基础

【10. 差分】

js成绩奖惩例题

L'introduction en bourse de Wild Wind Pharmaceutical a pris fin: Yu pinzeng, qui avait l'intention de lever 540 millions de RMB, a effectué un investissement P2P.

【c ++ primer 笔记】第4章 表达式

再见了,敏捷Scrum
![[compilation principles] review outline of compilation principles of Shandong University](/img/a6/b522a728ff21085411e7452f95872a.png)
[compilation principles] review outline of compilation principles of Shandong University

无论LCD和OLED显示技术有多好,都无法替代这个古老的显示数码管
随机推荐
JDBC读取Mysql数据列表
Mobile security tools -jad
爬一个网页的所有导师信息
R 中的 RNA-Seq 数据分析 - 调查数据中的差异表达基因!
Custom palette for ggplot2
基础知识 | js基础
JS performance reward and punishment examples
Delay queue `delayqueue`
语音信号处理-概念(二):幅度谱(短时傅里叶变换谱/STFT spectrum)、梅尔谱(Mel spectrum)【语音的深度学习主要用幅度谱、梅尔谱】【用librosa或torchaudio提取】
js打印99乘法表
How can the flower e-commerce 2.0 era go after the breakthrough from 0 to 1?
剑指 Offer 07. 重建二叉树
JS output all prime numbers between 1-100 and calculate the total number
Manim math engine
PostgreSQL encounters permission denied in Windows system
再见了,敏捷Scrum
Window right click management
JDBC参数化查询示例
How to view program running time (timer) in JS
期货反向跟单—交易员的培训问题