问题
i'm trying to implement a custom version of an Observer pattern in c++.
These are my classes (just interface):
class Observer{
public:
void observe(std::string behaviour, Observable * observable);
void stopObserving(std::string behaviour, Observable * observable);
virtual void notified(std::string behaviour, Observable * observed);
// ...
};
class Observable{
public:
void notifyBehaviour(std::string behaviour);
// ...
};
And can be used like this:
class A : public Observer{
public:
void notified(std::string behaviour, Observable * observed){
if (behaviour == "jumping"){
// ...
}
}
};
class B : public Observable{
public:
void jump(){
// ...
notifyBehaviour("jumping");
}
};
int main(){
A a;
B b;
a.observe("jumping", &b);
b.jump();
// ...
}
Each class implementing Observer can register itself as observing an Observable with a behaviour.
The Observable can notify its actions with "notifyBehaviour" to everyone interested.
Each Observer listening will be notified via its method "notified".
The above works perfectly. Anyway in the example above:
void notified(std::string behaviour, Observable * observed){
if (behaviour == "jumping"){
// ...
}
i'd like to use the Observable * i'm passing to do something on the notifying object. Unfortunately i'll have to do something like this:
void notified(std::string behaviour, Observable * observed){
if (behaviour == "jumping"){
auto pointer = dynamic_cast<B *>(observed);
if (pointer)
pointer->doSomethingElse();
}
}
That's ugly and will probably create problems. For example observing two different jumping entities will need multiple castings waiting for the right one.
My question is: is it possible to pass some RTTI or call an overloaded function or a template having already the right object type? I'd like to have:
class A : public Observer{
public:
void notified(std::string behaviour, B * observed){
if (behaviour == "jumping"){
observed->doSomethingBlike();
// observed if of type B !
}
}
void notified(std::string behaviour, C * observed){
if (behaviour == "jumping"){
observed->doSomethingClike();
// observed if of type C !
}
}
};
so i just need to implement the various object types i'd like to listen.
I've tried with inheritance, templates, boost::any but still without luck.
Thanks for ideas.
回答1:
Declare a pure virtual method in the Observed class. Or just an empty virtual function so there's no need to override.
class Observed {
public:
virtual ~Observed();
Observed();
virtual void DoSomethingElse() {} // OR
//void DoSomethingElse() = NULL;
// rest of class
};
回答2:
I have done this recently in a couple of projects. The basic idea:
I created a base class (Notified) that has a virtual method for each interface you wish to receive:
class Notified { public: virtual bool Notify(NOTIFIED_EVENT_TYPE_T eventType, uint32 value) { return false; }; virtual bool Notify(NOTIFIED_EVENT_TYPE_T eventType, ITEM_ID_T value) { return false; }; virtual bool Notify(NOTIFIED_EVENT_TYPE_T eventType, const string& value) { return false; }; virtual ~Notified(); };
I created a singleton to act as a central point to hold which things you want to send messages to (derived from Notified) and methods to call them:
class Notifier : public SingletonDynamic<Notifier>
{
private:
... detail omitted...
public:
virtual void Reset();
virtual bool Init() { Reset(); return true; }
virtual void Shutdown() { Reset(); }
void Attach(Notified* observer, NOTIFIED_EVENT_TYPE_T eventType);
// Detach for a specific event
void Detach(Notified* observer, NOTIFIED_EVENT_TYPE_T eventType);
// Detach for ALL events
void Detach(Notified* observer);
// This template function (defined in the header file) allows you to
// add interfaces to Notified easily and call them as needed. Variants
// will be generated at compile time by this template.
template <typename T>
bool Notify(NOTIFIED_EVENT_TYPE_T eventType, const T& value)
{
...detail omitted
}
/* Used for CPPUnit. Could create a Mock...maybe...but this seems
* like it will get the job done with minimal fuss. For now.
*/
// Return all events that this object is registered for.
vector<NOTIFIED_EVENT_TYPE_T> GetEvents(Notified* observer);
// Return all objects registered for this event.
vector<Notified*> GetNotified(NOTIFIED_EVENT_TYPE_T event);
};
It is tempting to try and make a template method for the Notified class:
template <typename T>
virtual bool Notify(NOTIFIED_EVENT_TYPE_T eventType, const T& value)
But you cannot because it needs to be virtual so you can have your derived class handle it. I thought about digging deeper into doing it as a template (meta template stuff), but I think that would confuse an otherwise generally simple to use and understandable design.
The main point of this design is:
1. You get complete type safety calling your Notified derived classes, checked directly through the compile process.
2. If you don't implement a handler for your function, you can have the Notifier class throw an exception when you return false.
3. Writing code for each handler in a Notified derived class is trivial:
class MyNotified : public Notified
{
virtual bool Notified(NOTIFIED_EVENT_T evt, uint32 value)
{
bool result = true;
switch(evt)
{
case EVT_1:
...do something...
break;
default:
result = false;
}
return result;
}
};
I have examples of this in (in various stages of life) in posts found here.
Was this helpful?
来源:https://stackoverflow.com/questions/20057138/observer-pattern-with-type-information-c