Шаблон проектирования, позволяющий избежать потери при передаче сообщений

Базовый класс MessageHandler вывел классы. Они хотели бы передавать сообщения друг другу. Сообщения могут быть разных классов, но могут быть сделаны для общего класса. Как каждый может MessageHandler избежать передачи полученного сообщения? Можно ли как-то сделать что-то, что дает эффект параметризации виртуального шаблона? receiveMessage функция на MessageHandler?

По сути, я пытаюсь заменить следующий код чем-то, что не унывает, и, надеюсь, это вещь времени компиляции:

// ...
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 ответа

Решение

Это довольно легко сделать. Обычно есть две альтернативы:

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 может хранить что угодно, отсюда и название.

Он также имеет семантику значений, поэтому его можно копировать как его содержимое.

Если я правильно понимаю ваш вопрос, вам просто нужно прямое наследование с виртуальной функцией. Что-то вроде:

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

Выход:

  • Мне нравится сообщение!
  • Я ненавижу это сообщение!
  • Просто хорошо
  • Прости я занят
Другие вопросы по тегам