It's possible for the number of Observers to increase so much that the Subject gets overloaded, slowing down the time it takes to receive data from the server.
In this case, you put a middleman between the Subject and the Observers: ObserverQueue. The technique used to reduce the burden on the Subject goes like this uses two steps:
- Subscribe the Observers to ObserverQueue.
- Subscribe the ObserverQueue to the Subject.
Basically, ObserverQueue acts as a proxy Subject for the Observers, and ObserverQueue becomes the single Observer to actual Subject. ObserverQueue waits in its own thread and when the Subject updates, it comes out of its waiting state and notifies the real observers. Having only one Observer to update frees the Subject up to continue receiving messages from the network.
I've implemented ObserverQueue as a singleton, which allows one ObserverQueue per application. This technique allows you to have many-to-many relationships between Subjects and Observers, as shown in Figure 1.
|Figure 1. ObserverQueue: Implementing ObserverQueue as a singleton allows you to have many-to-many relationships between Subjects and Observers.|
You can subscribe the ObserverQueue as the Observer to as many Subjects as you want. You can also subscribe as many Observers to the ObserverQueue as you want. You even can update your Observer from more than one Subject. The ObserverQueue notifies all Observers with the data coming from all Subjects. The Observers can keep an array of Subject pointers from which they need data and while receiving data, they can check the Subject* that comes with MessageBase* with the pointers they have. If they find them equal, they can retrieve the data.
Words to the Wise
Making use of ObserverQueue is optional. Should you decide to use it then, you'll need to take care of and remember a few things:
Extending This Application
- Get the ObserverQueue pointer by using the ObserverQueue::CreateObserverQueue().
- After subscribing your Observers to the ObserverQueue, keep a reference to the ObserverQueue in your Observers. This is important because when Observers die, they need to call ObserverQueue::ReleaseQueue(). The ObserverQueue keeps count of its Observerswhen the count gets to zero it deletes itself.
- Keep the references to the Subjects in the Observers. Just in case.
- You can use Observer::AddSubject() to either store the reference to the ObserverQueue or the Subject. However, you still need to define one more function in your class to store references to both of these.
- The MessageBase* inclluded in update() contains the pointer to the Subject from which the data originated. The Subject* included in update() points to the ObserverQueue. So, in order to search for the data from the Subject, you need to check the Subject* of MessageBase*. You can use GetRealSubject() of it to get the Subject.
- In every Observer, whether it uses the data to update itself or not, you must call Done() on the MessageBase*. This tells the Message class when to destroy itself. In fact, MessageBase also keeps track of all the Observers it has to go through and when Observers call Done(), it reduces the count. When the count reaches zero it delete itself.
- To update your GUI component, you need to override Observer:: ShouldNotifyByMessage() and Observer::GetHandle(). Return true from the former and the handle of your window from the latter.
- Because this framework uses boost threads for multi-platform support, you need to have boost libs. The VC++ 6 project has settings to include boost files.
As you can see, the sample code
updates all Observers with data coming from all Subjects. This is not very efficient, because the Observer knows in advance from which Subject it wants to receive the data. Introducing a Change Manager to this framework keeps a map between the Subjects and the Observers interested in receiving their data. This allows the ObserverQueue to determine which Observers are interested in data from which Subject and update accordingly.
Another thing you may like to do is to embed ObserverQueue inside the Subject. This makes the users of this framework opaque to its internal representation.
The sample code doesn't inform Observers about Subject deletion, which may lead to dangling Subject pointers in the Observers. Adding a function to notify Observers about Subject deletion addresses this problem.