Как обработать различные версии протокола прозрачно в C++?

Это - очень общее (и часто злоупотребленный термин), но любое следующее было бы законными причинами назвать наследие приложения:

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

  2. (действительно 1a) кодовая база или платформа, на которой это создается, так стары, что быть квалифицированным или опытные разработчики для системы является и трудным и дорогим.

  3. поддержка приложений некоторый аспект бизнеса, который активно больше не выращивается и для которого изменения чрезвычайно редки, обычно для фиксации его, если что-то совершенно неожиданное переезжает он (канонический пример, являющийся относящейся к двухтысячному году проблемой) или если некоторое регулирование/внешнее давление вызывает его. Так как обе причины нажимают и обычно неизбежные, но никакая значительная разработка не произошла на проекте, вероятно, что те люди присвоили для контакта с этим, будет незнакомо с системой (и это накопило поведения и запутанность). В этих случаях это часто было бы причиной увеличить воспринятый и запланированный для риска, связанного с проектом.

  4. система/или, заменяется другим. Как таковой система может использоваться для намного меньше, чем первоначально предназначенный, или возможно только как средство просмотра исторических данных.

7
задан 3 revs 30 September 2009 в 12:38
поделиться

6 ответов

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

Поскольку это все разные версии одного и того же протокола, у вас, вероятно, могут быть общие вещи в базовом классе, а затем различия в подклассах. Другой подход может заключаться в том, чтобы базовый класс был для самой старой версии протокола, а затем каждая последующая версия имела класс, унаследованный от предыдущей версии. Это несколько необычное дерево наследования, но приятное в нем то, что оно гарантирует, что изменения, внесенные в более поздние версии, не повлияют на более старые версии. (Я'

10
ответ дан 6 December 2019 в 15:24
поделиться

Я использовал (и слышал о других) шаблоны для решения этой проблемы. Идея состоит в том, что вы разбиваете свои различные протоколы на базовые атомарные операции, а затем используете что-то вроде boost :: fusion :: vector для построения протоколов из отдельных блоков.

Следующее очень грубое (отсутствует много частей) пример:

// These are the kind of atomic operations that we can do:
struct read_string { /* ... */ };
struct write_string { /* ... */ };
struct read_int { /* ... */ };
struct write_int { /* ... */ };

// These are the different protocol versions
typedef vector<read_string, write_int> Protocol1;
typedef vector<read_int, read_string, write_int> Protocol2;
typedef vector<read_int, write_int, read_string, write_int> Protocol3;

// This type *does* the work for a given atomic operation
struct DoAtomicOp {
  void operator ()(read_string & rs) const { ... }
  void operator ()(write_string & ws) const { ... }
  void operator ()(read_int & ri) const { ... }
  void operator ()(write_int & wi) const { ... }
};

template <typename Protocol> void doProtWork ( ... ) {
  Protocl prot;
  for_each (prot, DoAtomicOp (...));
}

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

void doWork (int protocol ...) {
  switch (protocol) {
  case PROTOCOL_1:
    doProtWork<Protocol1> (...);
    break;
  case PROTOCOL_2:
    doProtWork<Protocol2> (...);
    break;
  case PROTOCOL_3:
    doProtWork<Protocol3> (...);
    break;
  };
}

Чтобы добавить новый протокол (который использует существующие типы), вы только необходимо определить последовательность протоколов:

typedef vector<read_int, write_int, write_int, write_int> Protocol4;

А затем добавить новую запись в оператор switch.

4
ответ дан 6 December 2019 в 15:24
поделиться

maybe oversimplified, but this sounds like a job for inheritance? A base class IProtocol which defines what a protocol does (and possibly some common methods), and then one implementation for IProtocol for each protocol you have?

0
ответ дан 6 December 2019 в 15:24
поделиться

I'd tend towards to using different classes to implement adapters for the different protocols to the same interface.

Depending on the protocol and the differences, you might get some gain using TMP for state machines or protocol details, but generating six sets of whatever code uses the six protocol versions is probably not worth it; runtime polymorphism is sufficient, and for most cases TCP IO is probably slow enough not to want to hard code everything.

0
ответ дан 6 December 2019 в 15:24
поделиться

Вам нужна фабрика протокола, которая возвращает обработчик протокола для подходящей версии:

ProtocolHandler&  handler = ProtocolFactory::getProtocolHandler(clientProtocolVersion);

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

Обратите внимание на то, что ProtocolHandler (базовый класс) в основном представляет собой стратегический шаблон. Как таковой, он не должен поддерживать свое собственное состояние (если требуется, это передается через методы, и stratergy обновит свойство объекта состояния).

Поскольку stratergy не поддерживает состояние, мы можем использовать ProtcolHandler между любым числом потоков, и поэтому владение не должно покидать фабричный объект. Таким образом, фабрике просто нужно создать один объект-обработчик для каждой версии протокола, которую она понимает (это можно даже сделать лениво).

0
ответ дан 6 December 2019 в 15:24
поделиться

Я соглашусь с Питом Киркхэмом, я думаю, что было бы довольно неприятно поддерживать потенциально 6 разных версий классов для поддержки разных версий протокола. Если вы можете это сделать, кажется, что было бы лучше, если бы более старые версии просто реализовывали адаптеры для перевода на последний протокол, чтобы у вас был только один рабочий протокол для обслуживания. Вы по-прежнему можете использовать иерархию наследования, как показано выше, но более старые реализации протокола просто выполняют адаптацию, а затем вызывают новейший протокол.

0
ответ дан 6 December 2019 в 15:24
поделиться
Другие вопросы по тегам:

Похожие вопросы: