Skip to main content
Code Review

Return to Revisions

2 of 8
Editing a sentance to give it more sense

C++ Observer design pattern implementation

I'm using the Observer design pattern in order to manage events in the game I'm currently developping.

I based myself on the implementation demonstrated at http://gameprogrammingpatterns.com/observer.html but improved it in order to ease it's use.

Here it is :

class Observer
{
public:
 virtual ~Observer() {}
 virtual void onNotify(Subject * entity, Event event) = 0;
private:
};
class Subject
{
public:
 Subject() {}
 virtual ~Subject() {}
 void addObserver(Observer* observer)
 {
 if (std::find(_observers.begin(), _observers.end(), observer) == _observers.end())
 {
 _observers.push_back(observer);
 }
 }
 void removeObserver(Observer* observer)
 {
 std::list<Observer*>::iterator it = std::find(_observers.begin(), _observers.end(), observer);
 if (it != _observers.end())
 {
 *it = NULL;
 _eraseQueue.push(it);
 }
 }
protected:
 void notify(Subject * entity, Event event)
 {
 for (std::list<Observer*>::iterator it = _observers.begin(); it != _observers.end(); ++it)
 {
 if (*it != NULL)
 (*it)->onNotify(entity, event);
 }
 while (!_eraseQueue.empty())
 {
 _observers.erase(_eraseQueue.front());
 _eraseQueue.pop();
 }
 }
 void notify(Subject * entity, Event event, Observer* observer)
 {
 if (observer != NULL)
 observer->onNotify(entity, event);
 }
private:
 std::list<Observer*> _observers;
 std::queue<std::list<Observer*>::iterator> _eraseQueue;
};
template <typename T>
class EventHandler : public Observer
{
public:
 virtual ~EventHandler() {}
 virtual void onNotify(Subject * entity, Event event)
 {
 if (dynamic_cast<T*>(this))
 {
 auto it = _actions.find(event);
 if (it != _actions.end())
 {
 (dynamic_cast<T*>(this)->*(it->second))(entity);
 }
 }
 }
protected:
 template <typename U>
 U safe_cast(Subject* entity)
 {
 if (dynamic_cast<U>(entity))
 return (dynamic_cast<U>(entity));
 else
 throw std::exception("Event thrown on not-matching entity");
 }
protected:
 std::map<const Event, void (T::*)(Subject *)> _actions;
};

And here's some explanations about what makes it special :

  • When removing an element from the observers list of a Subject, I'm storing the observers to remove into an erase queue, so that the iterator in the notify method still works even when removing an observer inside of the notify call.
  • "Observers" don't inherit from Observer directly, but inside they inherit from EventHandler wich is a templated class that allows me to automatically call the corresponding pointers to function when receiving an event, without having to manage it in every Observer's daugther classes.
  • The EventHandler class implements a safe_cast method so that I can convert the Subject received from onNotify can be easily and safely casted to their inherited class.

So, maybe you wonder why I'm using a EventHandler class instead of inheriting directly from the Observer class, but here's a quick exemple to show you why it's usefull and makes the code much easier to read :

MyClass::MyClass()
{
 _actions[GAME_STARTED] = &MyClass::gameStarted;
 _actions[CHARACTER_MOVED] = &MyClass::characterMoved;
}
void MyClass::gameStarted(Subject * entity)
{
 Game* game = safe_cast<Game*>(entity);
 // Do actions on the Game instance
}
void MyClass::characterMoved(Subject * entity)
{
 Character* character = safe_cast<Character*>(entity);
 // Do actions on the Character instance
}

That's it, my event handling is completly hidden from the final user, he just has to store the events that he wants to listen in his class, and it will automaticaly be handled by the EventHandler class.

I already now a few ways of improving my implementation of the Observer pattern :

  • Using smart pointers,
  • Actually, using references instead of pointers,
  • Handle the case where the Subject instance gets deleted during the notify method call.

But I'd like to have your opinion about the other things that I could improve, and your overall feel about this implementation :)

Thanks !

AntoineB
  • 313
  • 1
  • 3
  • 11
lang-cpp

AltStyle によって変換されたページ (->オリジナル) /