Browse DevX
Sign up for e-mail newsletters from DevX


Using the Observer Pattern to Update Dependent Objects : Page 2

The Observer pattern defines a one-to-many dependency between objects--when the central object changes state, all its dependents objects are notified and updated automatically. Master this pattern and you won't have to worry about managing consistency of state between components.




Building the Right Environment to Support AI, Machine Learning and Deep Learning

The Subject Class
Inheriting your classes from Subject and Observer eliminates the need to include the header files of your dependent objects in your central object's file. Simply include the Observer class' header in the Subject class and vice versa. This achieves decoupling, or the abstract coupling, between theses classes because it renders them aware only of the interfaces—not the implementations.

The Subject need not know the real Observers to keep a list of references to them. It can just keep a vector of the Observer* type and a references to all Observers.

To subscribe with the Subject, your Observers use the method Subject::AddObserver() of the Subject. To unsubscribe, use Subject::DeleteObserver(). The Subject is not at all concerned with the different types of dependent objects subscribing and unsubscribing to it. It has a list of Observers and it notifies them by using Observer::Update(). The Subject is further not concerned with how the Update () is implemented by the Observers. You can pass a reference to the Subject to your Obsever by using Observer::AddSubject().

This class included in this article's code provides the required implementation of all of the pure virtual functions of the Subject class. It stores the subscribed Observers in a thread-safe vector. Its functions are thread-safe. Making it thread-safe helps allows observers to get subscribed, unsubscribed, and notified safely.

Updating the Observers
The Observer::Update() is the single interface between the Subject and Observer and how the Subject notifies the Observers. One way to perform these notifications and updates us to use the pushsample code provides mainly the implementation of this method).

Using push, Observers do not need to be aware of the Subject interface for pulling data after getting notified. It requires only one method call.

One drawback of this method is that only the Observers, which are interested in common data or state change, can share this method. Another drawback is that you cannot send bulk data. You can solve this problem by keeping a pointer or reference to the Subject in the Observer. If the Observer is aware of the Subject, then it can call the functions of the Subject and pull the data.

If the Subject is receiving a different type of data than your Observers need, update the Observers by mixing the functionality of the template and virtual keywords. Normally, these keywords do not go together because template is a compile time activity and virtual is a runtime activity. The sample code mixes these by making the data going to the observers behave polymorphically.

To do this, the code uses two classes: MessageBase and MessageDetail:

class MessageBase { //-- }; template<class type> class MessageDetail : public MessageBase { \\-- };

When the Subject needs to notify the Observers, it packs the data, message, or changed state, in MessageDetail and passes it as a pointer to MessageBase to Observer::Update(). The Subject uses Subject::CreateMessage() to pack:

template<class type> MessageBase* CreateMessage(int msgtype, type data) { msg = new MessageDetail<type>(msgtype, this, data); return msg; }

Using this technique, the Subject is able to push any kind of data to the Observers.

Suppose the Subject needs to push string and int to the Observers. The following code shows what happens:

Messagebase* msg = CreateMessage(0, "DEVX"); Observer::Update(this, msg); msg = CreateMessage(0, 6); Observer::Update(this, msg);

When an Observer receives these messages, it uses RTTI to find its relevant data. Here's the Observer that is interested in receiving the string data:

Update(Subject* sub, MessageBase* msg) { if (sub == m_Sub) { if (MessageDetail<string>* m = dynamic_cast<MessageDetail<string>* >(msg)) { // will come here only the data is of string type } } }

This can be done for any kind of data.

What if many Observers need the same type of data, but depending on the different values, need to take action? You can use the argument msgtype (from CreateMessage()) to address this problem. msgtype forces Observers to recognize different values of the same data type and update themselves accordingly. msgtype is defined as int in MessageBase, but you can change it according to your needs.

Comment and Contribute






(Maximum characters: 1200). You have 1200 characters left.



Thanks for your registration, follow us on our social networks to keep up-to-date