Reputation: 5211
please excuse any and all newbieness),
I have the following task to perform:
I have several classes (call these A) that constantly gather information from various sources (database, internet, etc). They can contain a potentially large amount of information on some topic..
I also have a bunch of classes (call these B) that allow me to render a variety of graphs, if supplied with enough information (here, information can be as simple as one or more floating values).
Now, I have to tie these two things together, so that I can modify which information is displayed in which form with minimal changes. Now, I'm considering using an observer pattern here, because it seems to lend itself nicely to this problem. However, I'm a bit stuck and would like advice. I can clearly make classes A of a "data source" base type, that allows subscribers and notifies subscribers when there are changes. I can also make classes B of the type "observer" and let them subscribe to data sources and be notified of changes.
The problem is that I don't want my observers to know any particular type of information they are displaying. For instance, if I want to graph temperature in my city on the Y-axis and time on the X-axis, and I have a 2D-plot class (of type B), then I want to avoid any kind of A->GetTemperature ()
calls. Similarly, in class A, I don't want to call NotifyOfTemperatureChange ()
or anything like that...
One idea is to define a bunch of enums or strings like "temperature"
, "time"
, "humidity"
, etc and then tell the observer what it should listen to (something like A->SetYAxis (B, "temperature")
-- here, I'm informing class A that it should get a single float value for its Y axis from data source B, channel "temperature")
so that A can just do B->subscribeTo (whateverIPassedIn)
. That way, B doesn't need to know what information its plotting. A can then say notifyOfChangesOnThisChannel ("temperature")
. This, however, seems a bit hacky for me... For instance, would the data be actually passed to the notified function? Would I have to do something like this:
void B::subscriberChanged (int subscriberId, std::string channel)
{
float value = datasource [subscriberId].GetCurrentValue (channel);
}
or would be it something like
void B::subscriberChanged (int subscriberId, std::string channel, void *data)
{
float value = *static_cast <float *> (data);
}
and besides, how would B know the type of data? I mean in this example it's a float, but what if it's an int or a double?
I guess my question is: is this correct? Is there a nicer way to approach this problem?
Thank you in advance
Upvotes: 3
Views: 779
Reputation: 76778
You could solve this problem by making your observers template classes. You would then have to define a minimal interface (or concept actually) that you could call, say, a Fact
that would define what a single item of data looks like. You could also define a concept AxisDescription
that defines how axis are described (labels, units, linear/logarithmic etc).
Here a simple example to illustrate:
template<class FactType, class AxisType>
class B {
public:
b(AxisType axis); // initialization requires a description of the axis
// called when a new fact is available
void notify(FactType fact);
// called when many fact should be reported
template<class FactIterator>
void notify(FactIterator begin, FactIterator end);
};
FactType
can be as simple as float
or int
, but also more complex, depending on what you want to do with it.
Upvotes: 3