С++, как представлять разные этапы протокола с объектами
Это не столько технический вопрос, сколько вопрос дизайна С++.
Часто мне кажется, что мне приходится разрабатывать программы, которые должны управлять некоторым протоколом, который имеет какое-то соединение, этап синтаксического анализа и абстрактный вид. Обычно я стараюсь разрабатывать свои программы с разделением проблем на переднем плане.
Я продолжаю концы с "стеками" объектов, система сидит на столе синтаксического анализатора, который по очереди сидит на вершине соединения (часто есть больше слоев). Затем эти объекты используют вызовы функций-членов для вызова слоя под ним (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:
Еще одна проблема, о которой я забыл упомянуть, заключается в том, что вы в конечном итоге перенаправляете какой-то интерфейс, хотя стек, например, на верхнем уровне, все еще должен знать статус соединения.
Ответы
Ответ 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>>>
.