Recently I got into implementing a message dispatching system that uses the "Observer pattern": nothing special here. As I developed it I thought it would be nice to send "Message" objects from the "subject" that could be fundamentally different from each other and could be read from the many "observers".
These different messages took the form of different message classes (for example, think about the "User Logout message", "Screen mode toogle" and "Volume level changed", all of these need different information) and soon I found that the "observers" needed not to know about every different message I would want to create (that would be... unsustainable, to say the least). Instead, I would like each observer to be able to react to specific kinds of messages.
So in order to make something I thought that double dispatching could be my option here. After a litte I got this piece (c++11 only because of the for loop):
#include <iostream>
#include <vector>
#include <string>
/**
* A few forward declarations.
*/
class Message_base;
class Message_type_a;
class Message_type_b;
/**
* Base observer...
*/
class Observer_base
{
public:
/**
* All these implementations are empty so we don't have to specify them
* in every single derived class.
*/
virtual void get_message(const Message_base&) {}
virtual void get_message(const Message_type_a&) {}
virtual void get_message(const Message_type_b&) {}
};
/**
* Specification of all message types.
*/
class Message_base
{
public:
/**
* This is the method that will implement the double dispatching in all
* derived classes.
*/
virtual void be_recieved(Observer_base &r) const=0; //Now that's a nasty method name.
};
class Message_type_a:public Message_base
{
private:
int integer_value;
public:
Message_type_a(int v):integer_value(v) {}
int get_integer_value() const {return integer_value;}
void be_recieved(Observer_base &r) const {r.get_message(*this);}
};
class Message_type_b:public Message_base
{
private:
std::string string_value;
public:
Message_type_b(const std::string v):string_value(v) {}
std::string get_string_value() const {return string_value;}
void be_recieved(Observer_base &r) const {r.get_message(*this);}
};
/**
* This is the base clase for the Subject... Notice that there are no virtual
* methods so we could as well instantiate this class instead of derive it.
*/
class Subject_base
{
private:
std::vector<Observer_base *> observers;
public:
void emit_message(const Message_base& m) {for(auto o : observers) m.be_recieved(*o);} //Again, nasty to read since it's... backwards.
void register_observer(Observer_base * o) {observers.push_back(o);}
};
/**
* Now we will create a subject class for the sake of it. We could just call the
* public "emit_message" from main passing Message objects.
*/
class Subject_derived:public Subject_base
{
public:
void emit_message_a(int v) {emit_message(Message_type_a(v));}
void emit_message_b(const std::string v) {emit_message(Message_type_b(v));}
};
/**
* This gets fun... We make two observers. One will only get type_a messages
* and the other will get type_b.
*/
class Observer_type_a:public Observer_base
{
private:
int index; //We will use it to identify the observer.
public:
Observer_type_a(int i):index(i) {}
void get_message(const Message_type_a& m) {std::cout<<"Observer_type_a ["<<index<<"] : got type_a message : "<<m.get_integer_value()<<std::endl;}
};
class Observer_type_b:public Observer_base
{
private:
std::string name; //Merely to identify the observer.
public:
Observer_type_b(const std::string& n):name(n) {}
void get_message(const Message_type_b& m) {std::cout<<"Observer_type_b ["<<name<<"] : got type_b message : "<<m.get_string_value()<<std::endl;}
};
/**
* Stitch all pieces together.
*/
int main(int argc, char ** argv)
{
Observer_type_a o_a1(1);
Observer_type_a o_a2(2);
Observer_type_b o_b1("Sauron");
Observer_type_b o_b2("Roverandom");
Subject_derived s_a;
s_a.register_observer(&o_a1);
s_a.register_observer(&o_b1);
s_a.emit_message_a(23);
s_a.emit_message_b("this is my content");
s_a.register_observer(&o_a2);
s_a.register_observer(&o_b2);
s_a.emit_message_a(99);
s_a.emit_message_b("this is my second content");
//gloriously exit.
return 0;
}
For the sake of clarity I will speak my goals here:
- Be able to send many different messages from the Subject.
- Specialize the observers to they ignore every message not meant for them (it would be even nicer if they weren't sent at all, but I know I can make that registering different groups of observers).
- Avoid RTTI and derived class casting.
Here comes my question: Have I missed a simpler implementation to achieve my goals?.
It is important to mention that the system this will be used on will not have that many observers and maybe less than ten subjects present at the same time.
I think you should stick to what will be simpler. If all your observers handles all messages, then you must have one observer type. If the messages are unrelated, each observer mush watch only for the messages it handles.
A solution using Boost::Signal2 would be:
Running it, I get:
If you are going to use it, be sure to read the manual.