2

基类MessageHandler有派生类。他们想互相传递信息。消息可以属于不同的类,但可以共享一个基类。每个人如何MessageHandler避免向下转换收到的消息?是否有可能做一些具有模板参数化receiveMessageMessageHandler 上的虚函数的效果的事情?

本质上,我正在尝试用不会向下转换的代码替换以下代码,并且希望是编译时的事情:

// ...
virtual void MessageHandler::receiveMessage(Message &msg) = 0;
// ...

// to receive a message
void DerivedMessageHandler::receiveMessage(Message& msg)
{
    switch (msg.MsgType()) // enum
    {
        case Message::MessageType::A:
            MessageA& = dynamic_cast<MessageA&>(msg);
            break;

        case Message::MessageType::B:
            MessageB& = dynamic_cast<MessageB&>(msg);
            break;
        default:
            // don't process unknown messages
            break;
    }
}

// to send a message
list<MessageHandler> mhList;
// populate list
for (MessageHandler& mh : mhList)
{
    mh.receiveMessage(msg);
}

我知道我不能这样做,但是像

template <typename M>
void MessageHandler::receiveMessage(M& msg) {}

并且每个人都有DerivedMessageHandler专攻M?什么样的设计模式可以清晰地让每个处理程序处理其支持的消息对象?

4

3 回答 3

6

这很容易做到。一般有两种选择:

Boost.Variant

无需传递派生类,只需枚举消息可能的类型。这些类型不必相互派生。将这些类型包装在boost::variant中:

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

请注意,这意味着可能的消息数据类型必须是可枚举的。Boost.Variant 的访问方法使处理这些类型的对象变得容易,而无需确切知道它存储的是哪种类型。

Boost.Any

只需通过 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就像一个类型安全的void*。它会记住放入其中的确切类型,并且任何将其转换为存储在其中的其他内容的尝试都将失败。boost::any可以存储任何东西,因此得名。

它还具有值语义,因此可以像其内容一样被复制。

于 2012-02-17T01:17:07.290 回答
3

如果我正确理解您的问题,您只需要使用虚拟函数直接继承即可。就像是:

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 ...
    }
};

在您处理消息的地方,只需在收到的消息上调用 processMsg() 函数来处理每个类中指定的每条消息。

std::auto_ptr<BaseMessage> m(mailbox.getMessage()); // Returns whatever msg is sent to your handler
m->processMsg();
于 2012-02-17T01:05:51.290 回答
2

您可以使用访问者模式。

但是访问者应该知道每个子类型并为它定义一个动作,所以没有默认动作,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;
}  

输出:

  • 我喜欢这个消息!
  • 我讨厌这个消息!
  • 正好
  • 不好意思,我在忙
于 2012-02-17T01:39:30.610 回答