一:一个遍历问题导致的低效率范例
#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米内的玩家(列表内玩家)进行攻击。