Many applications involve observers waiting for notification. Getting the logic right for both publishers and subscribers is something you don't want to have to do repeatedly.
The need to notify several objects of some event is a common theme in application development. So common, in fact, that it is discussed in [1] as the Observer Pattern, a.k.a. Publish-Subscribe. The basic problem is to provide a means for an object known as the Subject to notify other objects, known as Observers, when some event occurs so that they can take appropriate action.
Some schemes to implement this include having all observers register an event handler via a function pointer, or having observers poll or query the subject to determine if the event has taken place. These schemes have drawbacks, however. It is difficult to pass data from the subject to the observers without resorting to pointers to void or some similar mechanism. And the frequent polling by observers for some possibly infrequent event can incur significant overhead.
I present here a simple, object-oriented, Standard C++ implementation of the Observer Design Pattern. It allows the creation of any number of named publications that any number of Observers can subscribe to. It provides type safety (no void pointers) and has no polling. It also enforces other requirements, such as only a publication owner (creator) can publish that publication. This implementation can, and in fact has been, plugged into existing code relatively non-intrusively and with little effort.
The implementation consists of three classes, Subject, Observer, and Publisher. A Subject is an object that needs to notify observers when some event occurs. A Subject can create or discontinue a named publication and trigger a publication to be published to subscribers (Observers.) An Observer is an object that needs to know when the event occurs and take some action. An Observer can subscribe and unsubscribe to a publication and receive updates when a subject triggers a publish event. The Subject and Observer must agree on the publication name in advance. The Publisher is a singleton object that manages all publications and acts as the intermediary between Subjects and Observers. Client code need not, in fact should not, use or even know about the Publisher.
An extract of the Subject class is shown in Figure 1. A Subject can create, discontinue, or publish any number of named publications. Objects that need to provide notifications to observers should (preferably) inherit from Subject, but could just contain an instance of it. This Subject instance must remain in scope throughout the lifetime of the publication, because only the publication owner (creator) may publish a given publication. When a Subject is destroyed, all of his publications are discontinued.
An extract of the Observer class is shown in Figure 2. Client code that needs to receive updates should inherit from Observer and define the pure virtual update function. This function will be called by the Publisher object when a Subject triggers a publish operation. The update function takes the publication name as a parameter, which allows an Observer to subscribe to multiple publications and take different actions for each. It also takes a Subject* as an argument. This allows the Observer to obtain more information from the Subject in a type-safe manner. The concrete observer can safely cast (using dynamic_cast) the Subject* to its derived type and call any public functions of the derived Subject.
The Observer class remembers all publications it has subscribed to so that, if it is destroyed, it can unsubscribe from them in its destructor. This is necessary, of course, because the Publisher is maintaining a pointer to all subscribed Observers, and this pointer will be invalidated when the Observer is destroyed. All this happens behind the scenes, with no intervention from the concrete observer.
The Publisher is the glue that ties it all together; it keeps track of all publications, their subscribers, and their owners. It will only allow the owner to publish any given publication. The Publisher is an instance of the Singleton design pattern [1] there is only one Publisher object. The Publisher class is illustrated in Figure 3.
The Publisher keeps a std::map of std::string's to instances of struct PubMapVal. A PubMapVal object has a Subject* (the publication owner) and an ObserverSet* (a std::set of Observer*'s.) This set contains a pointer to each subscribed observer.
In the intended use of these classes, client code would never interact directly with the Publisher. All publications are created, discontinued, and published via an instance of a Subject. All subscribes and unsubscribes are initiated with a concrete observer. This could be enforced more heavily by making all methods of Publisher private and declaring Subject and Observer friends of Publisher, but I did not like that approach.
Tying It Together
When a Subject calls Subject::createPublication, the call is forwarded to Publisher::createPublication. The Subject also passes its this pointer, which the Publisher records as the publication owner. When an Observer calls Observer::subscribe, the base Observer class forwards this call to the publisher along with its this pointer, which the Publisher adds to the set of all observers for that named publication. When a Subject calls Subject::publish, the call is forwarded to Publisher::publish along with the Subject's this pointer. The Publisher ensures that the Subject owns the publication and then calls the update function of each subscriber, passing the Observers the string publication name and a pointer to the subject who initiated the update.
Type safety and publication integrity are enforced in several ways. Only a Subject (or an object that creates a Subject) can create, discontinue, or publish a publication since a Subject* is a required parameter in Subject-related Publisher methods. Further, the Publisher will throw an exception if the Subject attempting to publish or discontinue a publication is not the publication owner. Similarly, only a concrete Observer can subscribe or unsubscribe to a publication since an Observer* must be passed to the corresponding Publisher methods. If a concrete observer needs more information from the Subject in its update method, it can safely cast the Subject to the appropriate derived type and call methods of the derived subject. If the observer subscribes to multiple publications, it can take different actions based on which publication is being published.
Note that a Subject could inherit from both class Subject and class Observer and then subscribe to its own publication.
Client code might look like the example in Figure 4.
Data Structures and Time Complexity
I chose a std::set for the Publisher's Observer collection and the Observer's collection of subscribed publications as a compromise in efficiency. The set allows logarithmic-time unsubscribes but makes subscribes also logarithmic, while a std::list would provide constant-time subscribes but linear-time unsubscribes. If, in your application, many Observers subscribe to a publication but rarely unsubscribe (and are not destroyed), then a list might be more appropriate. Note that with a list, however, you must take care to avoid an Observer subscribing to a single list multiple times (and thus having its update method called more than once for a single publish operation). With the set implementation, a second attempt to subscribe to the same publication has no effect, since a set must have unique items.
Conclusion
In this article, I have presented an easy-to-use, type-safe, and object-oriented implementation of the Observer design pattern. It is very flexible, allowing any number of named publications, each with any number of subscribers. A subscriber can subscribe to multiple publications and take the appropriate action for each in its update function. A subject can even subscribe to its own publication if it inherits from both Subject and Observer.
Plugging this into an existing application is relatively simple. The class that needs to publish should inherit from Subject and have the appropriate calls to createPublication and publish inserted. The classes that need to receive these publications must inherit from Observer, define the update function, and call subscribe with the known publication name. Client code need not worry about unsubscribing or discontinuing publications when an observer or subject is destroyed this happens automatically. Nevertheless, subjects and observers can discontinue or unsubscribe from publications if need be.
Reference
[1] Gamma, Helm, Johnson, and Vlissides. Design Patterns (Addison-Wesley, 1995).
Paul Barnes has a B.S. and M.S. in Applied Mathematics from the University of Tulsa. He currently works as a developer with StatSoft in Tulsa. Paul can be reached at pbarnes@statsoft.com.