Introduction
The Observer Pattern (also called the "Publish-Subscribe" or "Subject-Observer" Pattern) is one of the most well known patterns in developer community. There are also many articles talking about it. If you are not very familiar with the Observer pattern, you may like to check here first. In this article, I want to show a slightly more stable version of the Observer Pattern which is particularly useful and time-saving in dealing with dynamically changed subject-observer relationships.
In a standard implementation, a subject holds an array of pointers referring to the attached observers. An attached observer should explicitly "detach" itself from the subject before dying. So, the observers need to hold references back to the subject in some way. As you might realize, the two parties hold references to each other. What if any subject or observer dies in silence -- let's say, a thrown exception skips the 'detach' call. Dangling pointers!
My template implementation offers you: An observer doesn't hold references to subjects, and a subject can die silently before its attached observers. My templates also provide standard features -- automatically implementing bolstering codes (e.g., attaching/detaching and notifying).
Implementation
The attached source code should explain itself. Here, I would like to show some basic ideas.
As shown in the above figure, a double-direction list is used for each subject. The benefit of a double-direction list is that the observer can delink from the chain itself at any time. So the delink function can be called from a destructor of an observer, which ensures that no dangling pointer is left in the list.
The observer itself is actually not linked into the list directly. Instead, an observer owns an array of linkable nodes, which in turn link into the list. The nodes also act as proxy to forward notification messages to its owner, the observer.
Using the Code
Using the template is quite simple. Before I show you a simple example, make sure you have installed the boost library. Let's say in a stock market application, you need to write a price monitor (an observer) to alert the user when the latest trade price changes. Shown below is the interface that needs to be implemented.
struct PriceChangeListener
{
virtual void OnChange(double latest_price) = 0;
};
A price monitor looks like this:
class PriceMoniter : public LinkableObserverImpl<pricechangelistener />
{
virtual void OnChange(double latest_price)
{
cout << "Latest price changed to " << latest_price;
}
}
A content subject looks like this:
#include <boost/bind.hpp>
class PricePublisher : public SubjectSourceImpl<PriceChangeListener>
{
public:
void SetLastestPrice(double latest_price_))
{
NotifyChanges(double latest_price_));
}
private:
void NotifyChanges(double latest_price_)
{
TravelObservers(boost::bind(&PriceChangeListener::OnChange, _1, latest_price_)));
}
}
Note that all the attach and detach functions of PricePublisher
are provided by the SubjectSourceImpl
template. The only function the PricePublisher
needs to implement is NotifyChanges
, which calls the OnChange
function of the attached observers. Here, I'm using boost::bind
to simplify the calling.
The below code snippet shows how to attach observers to subjects.
PricePublisher subject_A;
{
PriceMoniter observer_1;
subject_A.Attach(&observer_1);
subject_A.SetLastestPrice(2.35); }
PriceMoniter observer_2;
subject_A.Attach(&observer_2);
subject_A.SetLastestPrice(2.40);
The local variable observer_1
is inside a block, which will be destroyed right out of the block. You can see that there is no need to "detach" observer_1
.
As you might notice, the code is shorter than a standard Observer Pattern implementation. So, have your fun with these templates.
Notice
- Try not to create Observers and Subjects in different threads, as I didn't implement any mutex operation when operating the linked list. Of course, you are free to add this feature on your own.
- I use the boost library to ease my daily life. If you are not comfortable with the boost library, also feel free to change to a boost-less version.