Подтвердить что ты не робот

С++, как представлять разные этапы протокола с объектами

Это не столько технический вопрос, сколько вопрос дизайна С++.

Часто мне кажется, что мне приходится разрабатывать программы, которые должны управлять некоторым протоколом, который имеет какое-то соединение, этап синтаксического анализа и абстрактный вид. Обычно я стараюсь разрабатывать свои программы с разделением проблем на переднем плане.

Я продолжаю концы с "стеками" объектов, система сидит на столе синтаксического анализатора, который по очереди сидит на вершине соединения (часто есть больше слоев). Затем эти объекты используют вызовы функций-членов для вызова слоя под ним (Tx) и используют обратные вызовы (std::function, обычно) для захвата информации, поступающей из других направлений (Rx).

Этот дизайн кажется действительно подпарам, так как он добавляет сложности, и каждый уровень должен иметь постепенно увеличивающийся конструктор и так далее. Кроме того, поскольку соединение обычно использует что-то вроде ASIO, обратные вызовы обычно выполняются на разных потоках, поэтому трудно рассуждать о безопасности потоков.

Существует ли проектный паттерм или идиома, которые лучше всего представляют эту структуру/функциональность?

ИЗМЕНИТЬ

Простой пример

class basic_connection {
     basic_connection(std::string address);

     void send(std::string);
     std::function<void(std::string)> on_receive;
};

У меня есть несколько классов, подобных этому, которые сохраняют это состояние слоя и склеиваются вместе с их публичными функциями-членами и обратными вызовами.

Уровень выше этого, принимает процессы обработки команд для сети и вызывает basic_connection::send. И берет необработанные данные из basic_connection и преобразуется в команды для слоя над ним, который не обрабатывается.

EDIT2:

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

4b9b3361

Ответ 1

Без набора требований трудно что-либо рекомендовать. Однако из описания высокого уровня в вашем вопросе кажется, что вы можете использовать шаблон модели-просмотра-контроллера, возможно, в сочетании с другими. Помните, шаблоны дизайна - ваши друзья, и именно вы решаете, подходит ли использование и в какой степени. Дизайн шаблона очень легко злоупотреблять, и это происходит постоянно.

Ответ 2

Похоже, что вы пытаетесь сделать то, что обычно называют "конструированием конвейера".

Вот простой способ подключения двух слоев:

class I
{
    virtual void a() = 0;
    virtual void b() = 0;
}

class X
{
    I& m_i;

    X(I& i) : m_i(i) {}

    void onRecv(const char* data, size_t len)
    {
        for (size_t p = 0; p < len; p++)
            switch (data[p])
            {
            case 'a': m_i.a(); break;
            case 'b': m_i.b(); break;
            }
    }
}

class Y : public I
{
    void a() { ... }
    void b() { ... }
}

int main()
{
    X x;
    Y y(x);

    while (...)
        x.onRecv(data,len);
}

Ответ 3

Мне кажется, что вам нужна дополнительная абстракция. Я бы начал с разработки общего типа для описания того, что на самом деле является слоем и (при необходимости) уточняет, что для каждого конкретного уровня, не принимая во внимание ваши конкретные протоколы для этих слоев. Итак, вы можете сказать, что для протокола layer-k нужен объект типа layer- (k-1).

Из вашего описания я предполагаю, что ваши более высокие слои создают свой нижний нижний слой, который заставляет конструкторы раздуваться. Просто попросите ссылку (возможно, лучше всего реализована с помощью shared_ptr или unique_ptr) для следующего нижнего уровня в вашем конструкторе и попросите пользователя интерфейса беспокоиться о его создании.
Поскольку вы определили абстрактный интерфейс, вы все равно можете использовать более низкий уровень полиморфно, не беспокоясь о том, как он реализован и какой конкретный протокол нижнего уровня используется.

Для приема вам обычно требуются обратные вызовы, которые могут быть реализованы одинаково. Вы даже можете установить их в конструкторе объектов более высокого уровня и удалить их в деструкторе.


Если вы знаете во время разработки, какой протокол будет играть с другим протоколом, вы также можете заменить полиморфные вызовы, сделав реализацию протокола шаблоном, который получает свой более низкий протокол примерно так: Tcp<Ip<Ethernet<Device>>>.