设计模式以避免在消息传递中向下转换

时间:2012-02-17 00:59:14

标签: c++ templates design-patterns template-specialization downcast

基类MessageHandler具有派生类。他们想把信息传递给对方。消息可以是不同的类,但可以使其共享基类。每个MessageHandler如何避免向下传播收到的消息?是否有可能在MessageHandler上执行模板参数化虚拟receiveMessage函数的操作?

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

// ...
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?什么是干净利落地让每个处理程序在其支持的消息对象上工作的设计模式?

3 个答案:

答案 0 :(得分:6)

这很容易做到。通常有两种选择:

Boost.Variant

不是传递派生类,而是简单地枚举消息可能的类型。这些类型不需要彼此派生。将这些类型换成boost::variant

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

请注意,这意味着必须可枚举可能的消息数据类型。 Boost.Variant的访问方法可以轻松处理这些类型的对象,而无需确切知道它存储的类型。

Boost.Any

只需传递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可以存储任何内容,因此可以存储名称。

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

答案 1 :(得分: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();

答案 2 :(得分: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;
}  

输出:

  • 我喜欢这条消息!
  • 我讨厌这条消息!
  • 很好
  • 抱歉,我很忙