C++观察者模式的应用

一、观察者模式简介

观察者模式是让多个观察者监听同一个主题对象(被观察者), 主题对象状态发生变化时,会通知所有的观察者,使它们能够更新自己。

二、观察者模式在游戏开发中的应用

1.设计理念

我们设计一个Demo用来加深对观察者模式的认识。
在C++的游戏开发中,设计模式对于各模块来说尤为重要。在经典的RPG游戏中,观察者模式可以应用到英雄状态的监听上,将英雄类对象作为主题对象(被观察者),怪物类、控制类、视角类(摄像机类)等可作为观察者随时监听英雄的状态以作出自身状态的更新。接下来的示例将主要展示观察者监听英雄在地图中的坐标。
在这里插入图片描述

2.代码示例

2.1 首先定义一个抽象观察者类

定义抽象观察者类,以抽象出诸如摄像机类、控制类、怪物类等不同具体观察者类。

class Observer {
public:
	Observer() = default;
	Observer(const string& name) : name_(name) {};
	virtual ~Observer() = default;
	
	void Call(Object* obj) {
		obj->Notify(GetName());
	}
	
	//声明抽象方法
	virtual void Do(Object* obj) = 0;

	string GetName() const {
		return name_;
	}

	void SetName(string name) {
		name_ = name;
	}

private:
	string name_;
};

2.2 定义具体观察者——摄像机类

class Camera : public Observer {
public:
	Camera() = default;	
	Camera(const string& name) : Observer(name) {};
	
	//重写父类方法
	virtual void Do(Object* obj) override {
		//在这里重新设置摄像机的观察位置
		cout << GetName() << ":摄像机观察位置已更新,对象已显示在屏幕中间" << endl;
	}
	
private:
	//禁用拷贝构造和赋值拷贝
	Camera(const Camera&) = delete;
	Camera& opereator =(const Camera&) = delete;
};

2.3 定义具体观察者——控制类

class Control : public Observer {
public:
	Control() = default;	
	Control(const string& name) : Observer(name) {};
	
	//重写父类方法
	virtual void Do(Object* obj) override {
		//在这里判断对象是否越界,并作出控制反馈
		cout <<  GetName() << ":对象即将越界,移动停止" << endl;
		//cout << "对象未越界,继续移动" << endl;
	}
	
private:
	//禁用拷贝构造和赋值拷贝
	Control(const Control&) = delete;
	Control& opereator =(const Control&) = delete;
};

2.4 定义具体观察者——怪物类

class Monster : public Observer {
public:
	Monster() = default;	
	Monster(const string& name) : Observer(name) {};
	
	//重写父类方法
	virtual void Do(Object* obj) override {
		//在这里根据对象坐标,计算接近路径
		cout << GetName() << ":路径重新计算成功" << endl;
	}
	
private:
	//禁用拷贝构造和赋值拷贝
	Monster(const Monster&) = delete;
	Monster& opereator =(const Monster&) = delete;
};

2.4 定义抽象被观察者类——实体类

有时我们的被观察者不应该只是玩家所控制的英雄,可能是别的什么,为了代码的扩展性着想,对被观察者也要进行抽象。

class Object {
public:
	
	Object() = default;
	virtual ~Object() = default;
	// 声明抽象方法
	virtual void Notify(Object* obj) = 0;
	

private:
	vector<Observer*> observers_;
};

2.5 定义具体被观察者类——英雄类

class Hero : public Object {
public:
	
	Hero() = default;
	// 声明抽象方法
	virtual void Notify(Object* obj) = 0;
	

private:
	vector<Observer*> observers_;
};

为了方便,我将上述所有类的定义放在了同一个文件,名为Observer.h

//Observer.h
#include <iostream>
#include <vector>

using namespace std;

//前向声明
class Observer;

class Object {
public:

	Object() = default;
	virtual ~Object() = default;
	// 声明抽象方法
	virtual void Notify(const string& name) = 0;

	void AddObserver(Observer* obj) {
		observers_.push_back(obj);
	}

protected:
	vector<Observer*> observers_;
};

class Observer {
public:
	Observer() = default;
	Observer(const string& name) : name_(name) {};
	virtual ~Observer() = default;

	void Call(Object* obj) {
		obj->Notify(GetName());
	}

	//声明抽象方法
	virtual void Do(float posx, float posy) = 0;

	string GetName() const {
		return name_;
	}

	void SetName(string name) {
		name_ = name;
	}

private:
	string name_;
};


class Camera : public Observer {
public:
	Camera() = default;
	Camera(const string& name) : Observer(name) {};

	//重写父类方法
	virtual void Do(float posx, float posy) override {
		//在这里重新设置摄像机的观察位置
		cout << GetName() << ":摄像机观察位置已更新,对象已显示在屏幕中间" << endl;
	}

private:
	//禁用拷贝构造和赋值拷贝
	Camera(const Camera&) = delete;
	Camera& operator = (const Camera&) = delete;
};

class Control : public Observer {
public:
	Control() = default;
	Control(const string& name) : Observer(name) {};

	//重写父类方法
	virtual void Do(float posx, float posy) override {
		//在这里判断对象是否越界,并作出控制反馈
		cout << GetName() << ":对象即将越界,移动停止" << endl;
		//cout << "对象未越界,继续移动" << endl;
	}

private:
	//禁用拷贝构造和赋值拷贝
	Control(const Control&) = delete;
	Control& operator = (const Control&) = delete;
};

class Monster : public Observer {
public:
	Monster() = default;
	Monster(const string& name) : Observer(name) {};

	//重写父类方法
	virtual void Do(float posx, float posy) override {
		//在这里根据对象坐标,计算接近路径
		cout << GetName() << ":路径重新计算成功" << endl;
	}

private:
	//禁用拷贝构造和赋值拷贝
	Monster(const Monster&) = delete;
	Monster& operator =(const Monster&) = delete;
};

class Hero : public Object {
public:
	
	//通知方法
	virtual void Notify(const string& name) {
		size_t size = observers_.size();
		for (size_t i = 0; i < size; ++i) {
			if (name == observers_[i]->GetName()) {
				observers_[i]->Do(posx_, posy_);
			}
		}
	}

private:
	float posx_, posy_;
};

2.5 main.cpp测试代码

#include "Observer.h"

int main() {

	Camera ca("camera"); //创建摄像机
	Control co("controller"); //创建控制类对象
	Monster mo1("monster1"); //创建怪物1
	Monster mo2("monster2"); //创建怪物2
	Monster mo3("monster3"); //创建怪物3
	Monster mo4("monster4"); //创建怪物4
	Monster mo5("monster5"); //创建怪物5

	Hero hero; //创建英雄
	//添加观察者
	hero.AddObserver(&ca);
	hero.AddObserver(&co);
	hero.AddObserver(&mo1);
	hero.AddObserver(&mo2);
	hero.AddObserver(&mo3);
	hero.AddObserver(&mo4);
	hero.AddObserver(&mo5);

	//监听被观察者
	ca.Call(&hero);
	co.Call(&hero);
	mo1.Call(&hero);
	mo2.Call(&hero);
	mo3.Call(&hero);
	mo4.Call(&hero);
	mo5.Call(&hero);

	return 0;
}

2.5 输出结果

在这里插入图片描述

3 总结

上述代码Demo只是为了展示观察者模式的设计思路,并没有将具体的方法完整实现,虽然上述Demo并不是最好的体现观察者模式的应用场景,但希望能对各位读者起到理解上的帮助。在群体通知下(上述代码属于观察者主动调用的单个通知)观察者模式的观察者数量最好不要太多,不然性能会大打折扣。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值