C++ 如何安全地实现观察者模式?
我正在实现一种类似于多线程俄罗斯方块游戏的观察者设计模式的机制。有一个包含EventHandler对象集合的游戏类。如果一个类想将自己注册为游戏对象的侦听器,它必须继承Game::EventHandler类。在状态更改事件中,在每个侦听器的EventHandler接口上调用相应的方法。这是代码的样子:C++ 如何安全地实现观察者模式?,c++,observer-pattern,C++,Observer Pattern,我正在实现一种类似于多线程俄罗斯方块游戏的观察者设计模式的机制。有一个包含EventHandler对象集合的游戏类。如果一个类想将自己注册为游戏对象的侦听器,它必须继承Game::EventHandler类。在状态更改事件中,在每个侦听器的EventHandler接口上调用相应的方法。这是代码的样子: class Game { public: class EventHandler { public: EventHandler(); vir
class Game
{
public:
class EventHandler
{
public:
EventHandler();
virtual ~EventHandler();
virtual void onGameStateChanged(Game * inGame) = 0;
virtual void onLinesCleared(Game * inGame, int inLineCount) = 0;
private:
EventHandler(const EventHandler&);
EventHandler& operator=(const EventHandler&);
};
static void RegisterEventHandler(ThreadSafe<Game> inGame, EventHandler * inEventHandler);
static void UnregisterEventHandler(ThreadSafe<Game> inGame, EventHandler * inEventHandler);
typedef std::set<EventHandler*> EventHandlers;
EventHandlers mEventHandlers;
private:
typedef std::set<Game*> Instances;
static Instances sInstances;
};
void Game::RegisterEventHandler(ThreadSafe<Game> inGame, EventHandler * inEventHandler)
{
ScopedReaderAndWriter<Game> rwgame(inGame);
Game * game(rwgame.get());
if (sInstances.find(game) == sInstances.end())
{
LogWarning("Game::RegisterEventHandler: This game object does not exist!");
return;
}
game->mEventHandlers.insert(inEventHandler);
}
void Game::UnregisterEventHandler(ThreadSafe<Game> inGame, EventHandler * inEventHandler)
{
ScopedReaderAndWriter<Game> rwgame(inGame);
Game * game(rwgame.get());
if (sInstances.find(game) == sInstances.end())
{
LogWarning("Game::UnregisterEventHandler: The game object no longer exists!");
return;
}
game->mEventHandlers.erase(inEventHandler);
}
virtual void Notify(Notifier::NOTIFIED_EVENT_TYPE_T eventType, int value);
virtual void Notify(Notifier::NOTIFIED_EVENT_TYPE_T eventType, const string& str);
类游戏
{
公众:
类事件处理程序
{
公众:
EventHandler();
虚拟~EventHandler();
虚拟无效状态已更改(游戏*inGame)=0;
虚拟虚空在线清理(Game*inGame,int inLineCount)=0;
私人:
EventHandler(consteventhandler&);
EventHandler&运算符=(const EventHandler&);
};
静态无效注册表venthandler(线程安全inGame、EventHandler*inEventHandler);
静态void UnregisterEventHandler(线程安全inGame、EventHandler*inEventHandler);
typedef std::set EventHandlers;
事件处理程序Meventhandler;
私人:
typedef std::set实例;
静态实例;
};
void Game::RegisterEventHandler(线程安全inGame,EventHandler*inEventHandler)
{
ScopeDreanderWriter rwgame(inGame);
Game*Game(rwgame.get());
if(sInstances.find(game)=sInstances.end())
{
LogWarning(“Game::RegisterEventHandler:此游戏对象不存在!”);
返回;
}
游戏->Meventhandler.insert(Inventhandler);
}
无效游戏::取消注册EventHandler(线程安全inGame、EventHandler*inEventHandler)
{
ScopeDreanderWriter rwgame(inGame);
Game*Game(rwgame.get());
if(sInstances.find(game)=sInstances.end())
{
LogWarning(“Game::UnregisterEventHandler:游戏对象不再存在!”);
返回;
}
游戏->Meventhandler.擦除(Inventhandler);
}
在这种模式下,我经常遇到两个问题:
- 工作线程中的游戏状态更改。我们希望通知发生在主线程中
- 该事件包装在boost::函数中,并作为PostMessage发送到主线程
- 不久之后,当游戏对象已被删除时,该函数对象由主线程处理。结果是一场崩溃
UnregisterEventHandler
成为一个静态方法,用于检查实例列表。这确实有帮助,但我觉得这是一个有点粗俗的解决方案
有人知道关于如何干净安全地实现通知程序/侦听器系统的一组指导原则吗?有没有关于如何避免上述陷阱的建议
注:如果你需要更多信息来回答这个问题,你可以在这里在线找到相关代码:
共享\u ptr
,以确保游戏最后被删除<>我写了很多C++代码,需要为我正在开发的游戏组件创建一个观察者。我需要一些东西来分发“开始帧”、“用户输入”等,作为游戏中的事件分发给感兴趣的各方。我也有同样的问题要考虑……一个事件的爆发会导致另一个观察者的毁灭,这也可能随后发生。我需要处理这件事。我不需要处理线程安全,但我通常追求的设计要求是构建足够简单(API方面)的东西,我可以在正确的位置放入一些互斥体,其余的应该自行处理 <>我也希望它是直的C++,不依赖于平台或特定的技术(比如Boost、Qt等),因为我经常在不同的项目中构建和重用组件(以及它们背后的想法)。 以下是我提出的解决方案的大致草图:
/*
The Notifier is a singleton implementation of the Subject/Observer design
pattern. Any class/instance which wishes to participate as an observer
of an event can derive from the Notified base class and register itself
with the Notiifer for enumerated events.
Notifier derived classes MUST implement the notify function, which has
a prototype of:
void Notify(const NOTIFIED_EVENT_TYPE_T& event)
This is a data object passed from the Notifier class. The structure
passed has a void* in it. There is no illusion of type safety here
and it is the responsibility of the user to ensure it is cast properly.
In most cases, it will be "NULL".
Classes derived from Notified do not need to deregister (though it may
be a good idea to do so) as the base class destrctor will attempt to
remove itself from the Notifier system automatically.
The event type is an enumeration and not a string as it is in many
"generic" notification systems. In practical use, this is for a closed
application where the messages will be known at compile time. This allows
us to increase the speed of the delivery by NOT having a
dictionary keyed lookup mechanism. Some loss of generality is implied
by this.
This class/system is NOT thread safe, but could be made so with some
mutex wrappers. It is safe to call Attach/Detach as a consequence
of calling Notify(...).
*/
class Notified;
class Notifier : public SingletonDynamic<Notifier>
{
public:
typedef enum
{
NE_MIN = 0,
NE_DEBUG_BUTTON_PRESSED = NE_MIN,
NE_DEBUG_LINE_DRAW_ADD_LINE_PIXELS,
NE_DEBUG_TOGGLE_VISIBILITY,
NE_DEBUG_MESSAGE,
NE_RESET_DRAW_CYCLE,
NE_VIEWPORT_CHANGED,
NE_MAX,
} NOTIFIED_EVENT_TYPE_T;
private:
typedef vector<NOTIFIED_EVENT_TYPE_T> NOTIFIED_EVENT_TYPE_VECTOR_T;
typedef map<Notified*,NOTIFIED_EVENT_TYPE_VECTOR_T> NOTIFIED_MAP_T;
typedef map<Notified*,NOTIFIED_EVENT_TYPE_VECTOR_T>::iterator NOTIFIED_MAP_ITER_T;
typedef vector<Notified*> NOTIFIED_VECTOR_T;
typedef vector<NOTIFIED_VECTOR_T> NOTIFIED_VECTOR_VECTOR_T;
NOTIFIED_MAP_T _notifiedMap;
NOTIFIED_VECTOR_VECTOR_T _notifiedVector;
NOTIFIED_MAP_ITER_T _mapIter;
// This vector keeps a temporary list of observers that have completely
// detached since the current "Notify(...)" operation began. This is
// to handle the problem where a Notified instance has called Detach(...)
// because of a Notify(...) call. The removed instance could be a dead
// pointer, so don't try to talk to it.
vector<Notified*> _detached;
int32 _notifyDepth;
void RemoveEvent(NOTIFIED_EVENT_TYPE_VECTOR_T& orgEventTypes, NOTIFIED_EVENT_TYPE_T eventType);
void RemoveNotified(NOTIFIED_VECTOR_T& orgNotified, Notified* observer);
public:
virtual void Reset();
virtual bool Init() { Reset(); return true; }
virtual void Shutdown() { Reset(); }
void Attach(Notified* observer, NOTIFIED_EVENT_TYPE_T eventType);
// Detach for a specific event
void Detach(Notified* observer, NOTIFIED_EVENT_TYPE_T eventType);
// Detach for ALL events
void Detach(Notified* observer);
/* The design of this interface is very specific. I could
* create a class to hold all the event data and then the
* method would just have take that object. But then I would
* have to search for every place in the code that created an
* object to be used and make sure it updated the passed in
* object when a member is added to it. This way, a break
* occurs at compile time that must be addressed.
*/
void Notify(NOTIFIED_EVENT_TYPE_T, const void* eventData = NULL);
/* Used for CPPUnit. Could create a Mock...maybe...but this seems
* like it will get the job done with minimal fuss. For now.
*/
// Return all events that this object is registered for.
vector<NOTIFIED_EVENT_TYPE_T> GetEvents(Notified* observer);
// Return all objects registered for this event.
vector<Notified*> GetNotified(NOTIFIED_EVENT_TYPE_T event);
};
/* This is the base class for anything that can receive notifications.
*/
class Notified
{
public:
virtual void Notify(Notifier::NOTIFIED_EVENT_TYPE_T eventType, const void* eventData) = 0;
virtual ~Notified();
};
typedef Notifier::NOTIFIED_EVENT_TYPE_T NOTIFIED_EVENT_TYPE_T;
相应的Notify(…)方法已添加到通知程序本身。所有这些都使用单个函数获取“目标列表”,然后对目标调用相应的函数。这样做效果很好,使接受者不必做难看的演员
这似乎很有效。解决方案发布在