Предотвращение неправильного использования конструкций, предназначенных исключительно для транспорта - PullRequest
1 голос
/ 16 марта 2011

Я работаю над продуктом, состоящим из нескольких компонентов, большинство из которых написаны на C ++, но один написан на C. У нас часто есть сценарии, когда часть информации проходит через каждый из компонентов через IPC.

Мы определяем эти сообщения, используя структуры, чтобы мы могли упаковать их в сообщения и отправить их через очередь сообщений.Эти структуры предназначены только для «транспортных» целей и написаны таким образом, что служат только этой цели.Проблема, с которой я сталкиваюсь, заключается в следующем: программисты держатся за структуру и используют ее как долгосрочный контейнер для информации.

На мой взгляд, это проблема, потому что:

1) Если мы изменим структуру транспорта, весь их код будет нарушен.Здесь должна быть инкапсуляция, чтобы мы не столкнулись с этим сценарием.

2) Структуры сообщений очень неудобны и предназначены только для передачи информации ... Кажется крайне маловероятным, что эта структура такжеОказалось, что это наиболее удобная форма доступа к этим данным (в долгосрочной перспективе) для этих других компонентов.

Мой вопрос: как я могу программно предотвратить это неправильное использование?Я хотел бы убедиться, что эти структуры могут использоваться только для транспорта.

РЕДАКТИРОВАТЬ: Я постараюсь привести пример здесь, как я могу:

struct smallElement {
    int id;
    int basicFoo;
};

struct mediumElement {
    int id;
    int basicBar;
    int numSmallElements;
    struct smallElement smallElements[MAX_NUM_SMALL];
};

struct largeElement {
    int id;
    int basicBaz;
    int numMediumElements;
    struct mediumElement[MAX_NUM_MEDIUM];
};

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

Ответы [ 5 ]

2 голосов
/ 16 марта 2011

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

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

msg_struct inputStruct = receiveMsg();
MsgClass msg(inputStruct);
msg.doSomething()
...
msg_struct outputStruct = msg.toStruct();

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

2 голосов
/ 16 марта 2011

Когда я определяю структуры сообщений (в C ++, недопустим в C), я удостоверяюсь, что:

  1. объект сообщения копируемый
  2. объект сообщения долженбыть построенным только один раз
  3. объект сообщения не может быть изменен после создания

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

Что нужно сделать для достижения этой цели:

  1. Есть один уникальный конструктор, который настраивает каждого члена
  2. все члены приватные
  3. имеют константные элементы доступа

Например, у вас может быть это:

struct Message
{
   int id;
   long data;
   Info info;
};

Тогда у вас должно быть это:

class Message // struct or whatever, just make sure public/private are correctly set
{
public:
   Message( int id, long data, long info ) : m_id( id ), m_data( data ), m_info( info ) {}

   int id() const { return m_id; }
   long data() const { return m_data; }
   Info info() const { return m_info; }

private:

   int m_id;
   long m_data;
   Info m_info;

};

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


ИЛИ .... Вы можете использовать «черный ящик» .

  1. Разделите слой сообщений в библиотеке, если это еще не сделано.
  2. Код клиента вообще не должен подвергаться определениям структуры сообщений.Поэтому не предоставляйте заголовки, не скрывайте их или что-то в этом роде.
  3. Теперь предоставьте функции для отправки сообщений, которые будут (внутри) создавать сообщения и отправлять их.Это даже поможет при чтении кода.
  4. При получении сообщений укажите способ уведомления клиентского кода.Но не предоставляйте сообщения прямо !!!Просто держите их где-нибудь (может быть, временно или используя правила жизни или что-то в этом роде) внутри вашей библиотеки, может быть, в виде менеджера, что угодно, но держите их ВНУТРИ ЧЕРНОГО Ящика.Просто предоставьте своего рода идентификатор сообщения.
  5. Предоставьте функции для получения информации из сообщений без раскрытия структуры.Для этого у вас есть несколько способов.Я бы в этом случае предоставил бы функции, собранные в пространстве имен.Эти функции запрашивают идентификатор сообщения в качестве первого параметра и возвращают только одно сообщение из сообщения (которое может быть полным объектом в случае необходимости).

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

С этим связаны две проблемы: очевидные затраты на производительность, и их написание и изменение явно тяжелее.Может быть, лучше использовать генератор кода.Google Protobuf полон хороших идей в этой области.


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

1 голос
/ 16 марта 2011

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

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

0 голосов
/ 03 сентября 2011

Я не могу сказать, что это проще, чем это: использовать буфер протокола . это сделает вашу жизнь намного проще в долгосрочной перспективе.

0 голосов
/ 16 марта 2011

обычно плохая идея определять транспортные сообщения как структуры. Лучше определить "нормальную" (полезную для программиста) структуру и сериализатор / десериализатор для нее. Для автоматизации кодирования сериализатора / десериализатора можно определить структуру с макросами в отдельном файле и автоматически сгенерировать typedef struct и сериализатор / десериализатор (может также помочь библиотека препроцессора boost)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...