Design pattern to avoid downcasting in message passing

前端 未结 3 1478
孤城傲影
孤城傲影 2021-02-06 18:39

Base class MessageHandler has derived classes. They would like to pass messages to each other. Messages could be of different classes, but can be made to share a ba

相关标签:
3条回答
  • 2021-02-06 19:29

    This is pretty easy to do. There are generally two alternatives:

    Boost.Variant

    Instead of passing a derived class, simply enumerate the possible types that a message could be. These types need not be derived from one another. Wrap those types in a boost::variant:

    typedef boost::variant<MessageData1, MessageData2, MessageData3, ...> MessageData;
    

    Note that this means that the possible message data types must be enumerable. Boost.Variant's visitation methods make it easy to work with objects of these types without knowing exactly which type it stores.

    Boost.Any

    Simply pass anything with a boost::any:

    void MessageHandler::receiveMessage(const boost::any &msg)
    {
      const MessageType1 *pMsg = boost::any_cast<MessageType1>(&msg);
      if(!pMsg)
        //Cannot process
        return;
    
      //Process message.
    }
    

    boost::any is like a type-safe void*. It remembers the exact type that was put into it, and any attempt to cast it to something other than what is stored in it will fail. boost::any can store anything, hence the name.

    It also has value semantics, so it can be copied like its contents.

    0 讨论(0)
  • 2021-02-06 19:32

    If I'm understanding your question correctly, you just need straight inheritance with a virtual function. Something like:

    class BaseMessage 
    {
        public:
        virtual ~BaseMessage() {}
    
        virtual void processMsg() = 0;
    };
    
    class MessageA : public BaseMessage
    {
        public:
        MessageA() {}
        virtual ~MessageA() {}    
        virtual void processMsg()
        {
            // ... do something for MessageA ...
        }
    };
    
    class MessageB : public BaseMessage
    {
        public:
        MessageB() {}
        virtual ~MessageB() {}    
        virtual void processMsg()
        {
            // ... do something for MessageB ...
        }
    };
    

    Where you handle the message, simply call the processMsg() function on the message you receive to process each message as specified in each class.

    std::auto_ptr<BaseMessage> m(mailbox.getMessage()); // Returns whatever msg is sent to your handler
    m->processMsg();
    
    0 讨论(0)
  • 2021-02-06 19:40

    You can use a visitor pattern.

    but a visitor should know each of subtypes and define an action for it, so no default action, AFAIK

    class Visitor;
    class BaseMsg {
    //..
    public:
    virtual void acceptVisitor(Visitor * v) = 0;
    };
    
    class Msg1;
    class Msg2;
    class Visitor {     
    // You can put here pure virtuals for sure every visitor will implement them
    public:
    virtual void action (Msg1 * msg) = 0;
    virtual void action (Msg2 * msg) = 0;
    };
    
    class Msg1: public BaseMsg {
    //..
    public:
    void acceptVisitor(Visitor * v){v->action(this);}
    };
    
    class Msg2: public BaseMsg  {
    //..
    public:
    void acceptVisitor(Visitor * v){v->action(this);}
    };
    
    
    
    class Visitor1 : public Visitor {
    // ...
    public:
    void action (Msg1 * msg) {/*...*/ cout << "I like the message!\n";}
    void action (Msg2 * msg) {/*...*/ cout << "I hate the message!\n";}
    // more messages and actions for them
    };
    
    class Visitor2 : public Visitor{
    // ...
    public:
    void action (Msg1 * msg) {/*...*/ cout << "Just fine\n";}
    void action (Msg2 * msg) {/*...*/ cout << "Sorry, I'm busy\n";}
    // more messages and actions for them
    };
    
    int main() {
    
    BaseMsg * a = new Msg1;
    BaseMsg * b = new Msg2;
    
    Visitor * act = new Visitor1;
    Visitor * lazy = new Visitor2;
    // ............
    // somewhere in a deep deep forest of your code
    
    a->acceptVisitor(act);
    b->acceptVisitor(act);
    
    // somewhere else
    
    a->acceptVisitor(lazy);
    b->acceptVisitor(lazy);
    
    delete act;
    delete lazy;
    delete a;
    delete b;
    return 0;
    }  
    

    Output:

    • I like the message!
    • I hate the message!
    • Just fine
    • Sorry, I'm busy
    0 讨论(0)
提交回复
热议问题