Шаблон проектирования, позволяющий избежать потери при передаче сообщений - PullRequest
2 голосов
/ 17 февраля 2012

Базовый класс 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 ]

6 голосов
/ 17 февраля 2012

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

Boost.Variant

Вместо передачи производного класса, просто перечислите возможные типы, которыми может быть сообщение.Эти типы не должны быть производными друг от друга.Оберните эти типы в boost :: variable :

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

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

3 голосов
/ 17 февраля 2012

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

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 голосов
/ 17 февраля 2012

Вы можете использовать шаблон посетителя.

, но посетитель должен знать каждый из подтипов и определять действие для него, поэтому никаких действий по умолчанию нет, 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;
}  

Вывод:

  • Мне нравится сообщение!
  • Я ненавижу это сообщение!
  • Отлично
  • Извините, я занят
...