Observer pattern with type information (C++)

社会主义新天地 提交于 2019-12-23 04:34:38

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!