在对象间定义一种一对多的依赖关系,以便当某对象的状态改变时,与它存在依赖关系的所有对象都能收到通知并自动进行更新。
(摘自《游戏编程模式》)
我们很熟悉的MVC模式,其底层就是观察者模式。Java中的java.util.Observer和C#的event更是将观察者模式集成于语言层面中。
要如何理解观察者模式,从目的上讲,观察者模式是为了使代码在逻辑层面更加统一。为何这么说呢?我们来看一个例子。在开发成就系统的时候,涉及获得成就条件的判定可以遍布所有的游戏逻辑(例如获得一万金币:涉及金币系统;坠落水中:涉及物理系统;行走100公里:涉及角色系统)。我们当然可以这么写:
//金币系统 class CoinSystem { void CoinSystemUpdate() { //金币系统相关代码... if(coinNum>10000) GetArchievements(ArchType.TenThousandCoins); } } //物理系统 class PhysicsSystem { void PhysicsSystemUpdate() { //物理系统相关代码... if(isDropWater) GetArchievements(ArchType.DropWater); } } //角色系统 class CharacterSystem { void PhysicsSystemUpdate() { //角色系统相关代码... if(walkLength>100) GetArchievements(ArchType.WalkHundredKilometer); } }
显而易见的是,我们在不同系统中插入了成就系统获得条件的判定代码。尽管很好理解,但这也导致了代码逻辑不统一,散落在各个模块中,既不优雅,也不便于维护。利用观察者模式可以对这些代码进行改进。
既然我们想要模块化上面的代码,我们就需要让各个系统的代码不再进行成就达成条件判定,而是将相关判定数据直接传递给成就系统(通常就是某个模块的实例)——尽管在系统中还是增添了与该系统无关代码(被观察者发出通知),但是比上面直接处理成就系统要优雅的多。下面我们将对代码进行改进。
成就系统作为观察者,它需要处理从被观察者发出来的消息,并根据消息类型处理相关逻辑。成就系统代码如下,所有的成就解锁条件将在成就系统接受消息的函数中进行判断:
//观察者基类 class Observer { public: virtual ~Observer(); virtual void OnNotify(const Object& sender,EventType event); } //成就系统 class Archievements : public Observer { public: virtual void OnNotify(const Object& sender,EventType event) { switch((ArchType)event) { case ArchType.TenThousandCoins: if(sender.coinNum>10000) Unlock(event); break; case ArchType.DropWater: if(sender.isDropWater) Unlock(event); break; case ArchType.WalkHundredKilometer: if(sender.walkLength>100) Unlock(event); break; //其他事件... } } private: void Unlock(ArchType type); }
被观察者需要怎么实现。被观察者就是需要将消息通知给观察者的,由于观察对象不只1个,因此,被观察者需要维护一个观察者列表。且不断地将消息发送给被观察者。实现代码如下图所示:
//被观察者基类 class Subject { private: vector<Observer*> observerList; public: void SendNotify(const Object& sender,EventType event) { for(int index=0;index<observerList.size();index++) { observerList[index].OnNotify(sender,event); } } public: void AddObserver(Observer* observer) { observerList.push_back(observer); } void RemoveObserver(Observer* observer) { for(int index=0;index<observerList.size();index++) { if(observerList[index]==observer) { observerList.erase(observerList.begin()+index); break; } } } } //我们可以使用"Have One"模式而非继承来处理 拓展成为被观察者。 class CoinSystem { public: Subject subject; void CoinSystemUpdate() { subject.SendNotify(this,ArchType.GetXXCoins); } } //只要观察者订阅了被观察者,消息就会不断被发送。 coinSystemInstance.subject.AddObserver(ArchievementsInstance); physicsSystemInstance.subject.AddObserver(ArchievementsInstance); characterSystemInstance.subject.AddObserver(ArchievementsInstance);
class Archievements : public Observer { public: Archievements() { //... coinSystemInstance.subject.AddObserver(ArchievementsInstance); physicsSystemInstance.subject.AddObserver(ArchievementsInstance); characterSystemInstance.subject.AddObserver(ArchievementsInstance); } ~Archievements() { //... coinSystemInstance.subject.RemoveObserver(ArchievementsInstance); physicsSystemInstance.subject.RemoveObserver(ArchievementsInstance); characterSystemInstance.subject.RemoveObserver(ArchievementsInstance); } }