Как правильно использовать сеттеры, когда объект, к которому осуществляется доступ, инкапсулируется более одного раза? - PullRequest
0 голосов
/ 05 октября 2018

Я очень часто борюсь с этим вопросом и не могу найти чёткого решения.Я думаю, что знаю мотивацию геттеров / сеттеров.

Предварительная информация:

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

// 1st stage data types ------------------------------
struct Cartesian
{
    int32_t x;
    int32_t y;
    int32_t z;
}

struct GeoLocation
{
    double_t latitude;
    double_t longitude;
    int32_t altitude;
}

// 2nd stage data types ------------------------------
struct Drone
{
    Cartesian baseOffset; // m
    Cartesian velocity; // m/s
}

struct Plane
{
    GeoLocation location; // semicircle
    Cartesian velocity; // knots
}

// 3rd stage data types ------------------------------
struct Swarm
{
    Plane base;
    Drone member[10];
}

В C ++ я использую классы вместо структур (потому что почему бы и нет?).И проблема возникает, когда данные о Swarm[3].member[8].velocity.x получены по некоторому каналу связи.Поймите, что в системе может быть больше одного роя.

Требование:

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

Вопрос:

Когда я использую методы получения и установки, я не могу сказать "Swarm[3].member[8].velocity.x«;вместо этого я могу сказать это несколькими способами:

1. Это недопустимо, поскольку функции get () возвращают ссылку на const и не могут вызвать set ().

Swarm[3].getMember(8).getVelocity().setX(5);(или set("X", 5))

2. Этот метод переносит всю нагрузку на класс Swarm.Хотя для тех, кто вызывает класс Swarm, код кажется короче, его очень сложно кодировать и выполнять обслуживание в фоновом режиме в случае изменения.

Swarm[3].setMemberVelocity(8,X,5)

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

Cartesian tempVelocity = Swarm[3].getMember(8).getVelocity();

tempVelocity.x = 5;

Swarm[3].setMemberVelocity(8, tempVelocity);

Какой из этих трех методов является лучшим?Или есть какие-нибудь альтернативы, которые я мог бы использовать?

Заранее спасибо.

Ответы [ 3 ]

0 голосов
/ 05 октября 2018

Возможно, я не знаю MISRA в полной мере.Тем не менее, кажется, что это гибко для гибкого дизайна (и вовсе не для c ++ ish).Предположим, я выяснил, что мне нужно:

struct SuperSwarm {
  Swarm swarms[10];
};

Затем, следуя вашему варианту 2, мне нужно будет реализовать множество сеттеров / геттеров для всех внутренних агрегатов, поскольку это звучит примерно так, как в вашем случае вам нужнобыть в состоянии установить все данные индивидуально.То же самое происходит, если вам нужно что-то изменить в Drone, то все должно быть обновлено.Для хорошего и гибкого дизайна вы можете увидеть, как это действительно кошмар.На самом деле не существует хорошей альтернативы соблюдению установленных вами правил.Вот почему вы обычно возвращаете неконстантную ссылку и используете ее, если вам не нужна специальная обработка.Это, в некотором смысле, защищает ваш код в будущем, а не только от публичной переменной-члена.

Один из способов, которым вы можете «обмануть» (и использовать свой вариант 3) и при этом быть гибким в плане дизайна, это проксивсе - как например std::shared_ptr.Когда вы возвращаете std::shared_ptr, кажется, что это копия, и это так - но на самом деле это просто прокси для того же самого объекта, на который указывает объект (что похоже на то, как все работает под капотом в некоторых других языках программирования ОО).Вам может понадобиться более подходящий прокси-класс.Но опять же зачем?почему бы просто не использовать структуры и говорить, что это структура данных , а не какой-то класс, несущий (другую) ответственность.В конце концов, тогда вы четко изложите свои намерения.

0 голосов
/ 10 октября 2018

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

. В качестве побочного эффекта такие данные также позволяют писать такой код:

swarms[swarmNo].drones[droneNo].velocity.x = 5;

Этот тип навигации по цепочкедо сих пор считается кодовым запахом и имеет также название «образец крушения поезда».Однако, по крайней мере, он не имеет лишних get, set и () раздувания для печати.

Все остальные данные должны следовать объектно-ориентированным принципам.Там сам рой несет ответственность за выполнение своих маневров, командуя своими беспилотниками, а сам дрон обязан следовать (или отказываться) от таких команд.Ничто не должно быть в состоянии просто установить какое-то отдельное свойство чего-то синего снаружи.

MISRA требует, чтобы данные такого типа объявлялись с использованием ключевого слова class.Нестатические элементы данных должны быть частными, а неконстантные дескрипторы этим элементам не должны возвращаться открытыми функциями-членами.

0 голосов
/ 05 октября 2018

Если он прибывает навалом (как в чанке для всех роев), у меня будут методы / члены, которые делегируют чанки подобъектам.Скажем, вы только что получили string update.Вы разделили данные для первого роя и позвонили swarm[0].update(chunk).Этот метод будет выполнять свою собственную проверку, а затем разделять блок на информацию для каждого члена и вызывать update для члена с меньшим фрагментом.В конце концов, вы получите до Cartesian, который сможет обновить X и все остальное.

...