避免消息传递中向下转型的设计模式

发布于 2025-01-06 05:25:30 字数 1197 浏览 1 评论 0原文

基类 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) {}

​​And have every DerivedMessageHandler专注于M?什么样的设计模式可以干净地让每个处理程序处理其支持的消息对象?

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 base class. How can each MessageHandler avoid downcasting a received message? Is it somehow possible to do something that has the effect of template-parametrizing the virtual receiveMessage function on MessageHandler?

Essentially, I'm trying to replace the following code with something that does not downcast, and is hopefully a compile-time thing:

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

I know I can't do this, but something like

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

And have each DerivedMessageHandler specialize on M? What would be a design pattern that cleanly lets each handler work on their supported message objects?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

红ご颜醉 2025-01-13 05:25:30

这很容易做到。通常有两种替代方案:

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 可以存储任何内容,因此得名。

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

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.

蛮可爱 2025-01-13 05:25:30

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

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

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();
段念尘 2025-01-13 05:25:30

您可以使用访客模式。

但访问者应该知道每个子类型并为其定义一个操作,因此没有默认操作,据我所知

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

输出:

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

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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文