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

Design pattern to avoid downcasting in message passing

本文关键字:转换 消息传递 设计模式      更新时间:2023-10-16

基类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吗?什么样的设计模式可以让每个处理程序在其支持的消息对象上工作?

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

Boost.Variant

与其传递派生类,不如简单地枚举消息可能的类型。这些类型不需要相互派生。将这些类型封装在增强中::变体:

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

请注意,这意味着可能的消息数据类型必须是可枚举的。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.
}

CCD_ 7类似于类型安全的CCD_。它会记住放入其中的确切类型,任何将其强制转换为其他的东西的尝试都将失败。boost::any可以存储任何东西,因此得名。

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

如果我正确理解您的问题,您只需要使用虚拟函数进行直接继承。类似于:

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();

您可以使用访问者模式。

但是访问者应该知道每个子类型并为其定义一个操作,所以没有默认的操作,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 finen";}
void action (Msg2 * msg) {/*...*/ cout << "Sorry, I'm busyn";}
// 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;
}  

输出:

  • 我喜欢这个消息
  • 我讨厌这个消息
  • 很好
  • 对不起,我很忙