Как определить тип сообщения во время выполнения? - PullRequest
0 голосов
/ 07 января 2019

В настоящее время я пишу систему обработки сообщений для моего игрового сервера. Я использую шаблон проектирования с двойной диспетчеризацией, чтобы выбрать нужный обработчик в соответствии с идентификатором сообщения, полученным из буфера данных. Но я столкнулся со следующей проблемой: мне нужно определить производный класс сообщения, чтобы передать его в объект обработчика двойной отправки. Единственное решение, которое я нашел, - написать метод фабрики в базовом классе с переключателем внутри, который создаст необходимый производный класс в соответствии с идентификатором сообщения.

class Message
{
public:
    void dispatch(Handler& handler)
    {
        dispatchImpl(handler);
    }

protected:
    virtual void dispatchImpl(Handler& handler) = 0;
};

template <typename TDerived>
class BaseMessage : public Message 
{
public:
    BaseMessage(unsigned short messageId)
        : id(messageId) {};

    virtual ~BaseMessage() = default;

    unsigned short getId() const { return id; }

    static std::unique_ptr<Message> create(BitStream& data)
    {
        switch (data.ReadShort())
        {
            case 1: return std::make_unique<FirstMessage>();
            case 2: return std::make_unique<SecondMessage>();
            case 3: return std::make_unique<ThirdMessage>();
            // ...
        }
    }

protected:
    unsigned short id;

    virtual void dispatchImpl(Handler& handler) override
    {
        handler.handle(static_cast<TDerived&>(*this));
    }
};

Как я могу улучшить свой дизайн, чтобы избежать заявления о большом переключении? Заранее спасибо.

1 Ответ

0 голосов
/ 07 января 2019

Вы можете использовать std::map<short,std::function<std::unique_ptr<Message> (BitStream& data)> и разрешить динамическую регистрацию этих фабричных функций:

std::map<short,std::function<std::unique_ptr<Message> (BitStream& data)> createMessageHandlers;

void registerCreateMessageHandler(short id,std::function<std::unique_ptr<Message> (BitStream& data)> fn) {
      createMessageHandlers[id] = fn;
}

и используйте его как

registerCreateMessageHandler(1, [](BitStream& data) { return std::make_unique<FirstMessage>();});
registerCreateMessageHandler(2, [](BitStream& data) { return std::make_unique<SecondMessage>();});

Ваши конструкторы типов сообщений, вероятно, должны принять BitStream& в качестве параметра конструктора.


Вам следует подумать об использовании такого инструмента, как буферы протокола Google , для определения ваших протоколов связи.
Это очень помогло бы в правильном версионировании и анализе пакетов сообщений (нейтральных по порядку байтов) по проводной (или беспроводной) сети.

В сочетании с, например, boost :: asio или zeromq для обработки транспортировки, что должно дать вам максимальную гибкость и надлежащие механизмы для разделения транспортного и семантического уровней коммуникации.

...