观察者(Observer)模式又称发布-订阅(Publish-Subscribe)模式,它描述的是一个一对多的关系,当一个对象改变时通知其他对应的观察对象做出对应的自动更新改变。类似我们读书时老师和学生的关系,学生就是观察者,老师就是被观察的对象,学生时刻注意着老师的状态变化而自动做出相应反应,用发布订阅来理解就是老师发布状态变化结果,学员订阅这些状态做出应对。
下面是观察者模式的UML图:
在实现观察者模式时主要就是要将观察者及观察主题的抽象接口定义好,将类的依赖关系转换到抽象的基类中去,这样就可以避免具体观察对象编译时的依赖关系,降低耦合;观察者就可以自动根据订阅的主题信息根据主题自动更新自己的状态。它的缺点在于当观察者较多时通知观察者时需要遍历而带来的性能开销,另外就是当观察者销毁前也需要被观察对象先删除观察者对象,不然遍历到已销毁对象会导致程序崩溃;
在实际的应用中常见的Qt中的信号与槽实现、MVC(模型视图控制)等都可以看成是一种观察者模式,我们这里以游戏中等级与装备、属性和技能为例构建了一个简单的观察者模式,等级就是具体的观察主题,而装备装备、属性和技能系统就可以看成是具体的观察者,我们可以自由的根据抽象的观察者接口去扩展更多具体观察者类;这些具体的观察者就可以根据观察的主题变化而自动适应更新。如下图等级的提升可带来属性、装备等的提升:
下面是具体的实现代码:
1. 抽象观察者类定义
class AbstractObject; //因为在观察者类中需要使用该类型,需在观察者类前声明
//抽象观察者类
class AbstractObserver
{
public:
AbstractObserver(AbstractObject *obj) : m_pObj(obj) { }
virtual void subscribeUpdate() = 0; //订阅更新接口
protected:
AbstractObject *m_pObj; //用于保存观察者观察对象
};
2. 抽象被观察对象类定义
class AbstractObject
{
public:
//为被观察对象增加观察者操作
void addObserver(AbstractObserver *obs)
{
m_obs_list.push_back(obs);
}
//为被观察对象删除观察者操作
void delObserver(AbstractObserver *obs)
{
m_obs_list.remove(obs);
}
//被观察对象发布通知给观察者的操作
void publishNotify()
{
//所有观察者根据发布通知自动订阅更新
for(const auto &obs : m_obs_list) {
obs->subscribeUpdate();
}
}
//获取被观察对象发布的主题信息接口
virtual int getSubject() = 0;
protected:
//用于保存所有相关的观察者对象的list
list<AbstractObserver *> m_obs_list;
};
3. 具体被观察对象类定义:角色等级类
class RoleLevel : public AbstractObject
{
public:
void setLevel(int level)
{
m_level = level;
}
virtual int getSubject()
{
return m_level;
}
private:
int m_level = 1;
};
4. 具体观察者类定义,这里可自由扩展
//具体观察者类:属性系统
class PropertySystem : public AbstractObserver
{
public:
PropertySystem(RoleLevel *lObj) : AbstractObserver(lObj) { }
virtual void subscribeUpdate()
{
RoleLevel *pObj = dynamic_cast<RoleLevel *>(m_pObj);
int level = pObj->getSubject();
if(level < 10) {
cout << "血量1000;物抗100;魔抗100" << endl;
}else if(level >= 10) {
cout << "血量2000;物抗150;魔抗150" << endl;
}
}
};
//具体观察者类:技能系统
class SkillSystem : public AbstractObserver
{
public:
SkillSystem(RoleLevel *lObj) : AbstractObserver(lObj) { }
virtual void subscribeUpdate()
{
RoleLevel *pObj = dynamic_cast<RoleLevel *>(m_pObj);
int level = pObj->getSubject();
if(level < 10) {
cout << "技能等级上限150" << endl;
}else if(level >= 10) {
cout << "技能等级上限300" << endl;
}
}
};
//具体观察者类:装备系统
class EquipSystem : public AbstractObserver
{
public:
EquipSystem(RoleLevel *lObj) : AbstractObserver(lObj) { }
virtual void subscribeUpdate()
{
RoleLevel *pObj = dynamic_cast<RoleLevel *>(m_pObj);
int level = pObj->getSubject();
if(level < 10) {
cout << "可装备皮甲" << endl;
}else if(level >= 10) {
cout << "可装备板甲" << endl;
}
}
};
5. 主函数功能测试代码
int main()
{
RoleLevel levelObj;
PropertySystem property(&levelObj); //具体观察者定义并订阅主题对象
SkillSystem skill(&levelObj);
EquipSystem equip(&levelObj);
levelObj.addObserver(&property); //订阅主题添加观察者对象
levelObj.addObserver(&skill);
levelObj.addObserver(&equip);
cout << ">>>>>>>>>>等级5<<<<<<<<<<" << endl;
levelObj.setLevel(5);
levelObj.publishNotify(); //通知观察者根据主题变化自动更新
// levelObj.delObserver(&equip);
cout << ">>>>>>>>>>等级10<<<<<<<<<<" << endl;
levelObj.setLevel(10);
levelObj.publishNotify();
return 0;
}
程序运行结果:
观察者模式也是一个非常常用的设计模式,需要我们多多熟悉,以上就是一个简单的实现测试,实际使用时可能会结合多种设计模式是的其结构变得复杂,所以就需要从最简单的开始,搞懂各种设计模式的结构及特点,才能真正做到融会贯通。