/* * Observer.hpp * * Created on: Jan 19, 2010 * Author: crueger */ #ifndef OBSERVER_HPP_ #define OBSERVER_HPP_ #include #include /** * Basic structure for the observer pattern * * Observers register themselves with the observables to be notified when something changes. * In the Observable code that changes, attributes should be started with OBSERVE;. This macro * locks the observer mechanism while changes are done. At the end of the scope in which the * macro was placed the lock is released. When the last lock is released all changes are * propagated to the observers. * * Each observerable can have sub-observables. When one of these sub-observables changes and * notifies its observers the observable that contains them will also notify its observers. * This passing on of updates is blocked, when the main-observable is in the process of * updating many of its internal sub-observables. This means the update is not passed, if * it is produced while the main-observable itself is within any Observation block. */ class Observable; class Notification; // Pointers to notifications are used for unique identification // using this typedef makes it hard for others to mess up this // identification process typedef Notification *const Notification_ptr; /** * An Observer is notified by all Observed objects, when anything changes. * * If a simple change is done to an Object the Obervable will call the update() method * of all signed on observers, passing itself as a parameter for identification. The * Observers should then react to the changes and update themselves accordingly. * * If an observed Object is destroyed it will call the subjectKilled() method * of all signed on Observers, again passing itself as a parameter for identification. * The Observers should handle the destruction of an observed Object gracefully, i.e. * set themselves inactive, display something else, etc. There is no need * to sign of from the dying object, since this will be handled by the Observable destructor. */ class Observer { friend class Observable; friend class Notification; public: Observer(); virtual ~Observer(); protected: /** * This method is called upon changes of the Observable */ virtual void update(Observable *publisher)=0; /** * This method is called when a special named change * of the Observable occured */ virtual void recieveNotification(Observable *publisher, Notification_ptr notification); /** * This method is called when the observed object is destroyed. */ virtual void subjectKilled(Observable *publisher)=0; }; /** * An Observable implements all neccessary method for being observed. * * That is, it provides methods for signing on and of from an * Observable that can be used by any observer. The actual * observer-mechanism is handled at a central static place * to avoid memory issues when many observable are around but only few * are actually observed. */ class Observable : public Observer { public: Observable(); virtual ~Observable(); /** * Sign an Observer on to this Observable. The Observer will be notified * whenever something inside the Observable changes. The Observer can * assign itself a priority for the changes in the range of -20:+20. * The Observer with lower priority will be called before the others, * same as with Unix nice-levels. This can be used when an Object * contains other objects that observe it (derived values), and these objects have * to recalculate their states before the changes should be propageted to the * UI. A default priority of 0 should be fine in most cases, since there is * ussually no need to order the update sequence. */ virtual void signOn(Observer *target, int priority=0); /** * Sign of a previously signed on Observer. After this no more * updates will be recieved from that observer. */ virtual void signOff(Observer *target); /** * Sign on for specialized notifications */ virtual void signOn(Observer *target, Notification_ptr notification); /** * Stop receiving a specialized notification */ virtual void signOff(Observer *target, Notification_ptr notification); /** * Ask an Observer if it is currently in a blocked state, i.e. if * Changes are in Progress, that are not yet published. */ virtual bool isBlocked(); protected: virtual void update(Observable *publisher); virtual void subjectKilled(Observable *publisher); virtual void notifyAll(); protected: // Observer mechanism is done from a static central place /** * Internal method. * Do not call directly. Use OBSERVE macro instead */ static void start_observer_internal(Observable *publisher); /** * Internal method. * Do not call directly. Use OBSERVE macro instead */ static void finish_observer_internal(Observable *publisher); static void enque_notification_internal(Observable *publisher, Notification_ptr notification); private: typedef std::multimap callees_t; typedef std::set notificationSet; static std::map depth; static std::map callTable; static std::map notifications; static std::set busyObservables; //! @cond // Structure for RAII-Style notification protected: /** * This structure implements the Observer-mechanism RAII-Idiom. * It triggers certain functions on creation and destruction so that * Observer mechanisms can be linked to scope block. */ class _Observable_protector { public: _Observable_protector(Observable *); ~_Observable_protector(); private: Observable *protege; }; //! @endcond }; class Notification { friend class Observable; public: Notification(Observable *_owner); virtual ~Notification(); protected: void addObserver(Observer *target); void removeObserver(Observer *target); void notifyAll(); private: Observable * const owner; std::set targets; }; // extra macro is necessary to work with __LINE__ #define PASTE(a,b) PASTE_HELPER(a,b) #define PASTE_HELPER(a,b) a ## b #define OBSERVE Observable::_Observable_protector PASTE(_scope_obs_protector_,__LINE__)(this) #define NOTIFY(notification) do{Observable::enque_notification_internal(this,notification);}while(0) #endif /* OBSERVER_HPP_ */