Совместное использование класса данных в потоке

Совместное использование класса данных в потоке
Совместное использование класса данных в потоке - frani @ Unsplash

Предположим, что существует поток функций на C++

step1();
step2();
step3();
step4();
step5();

и они взаимодействуют, добавляя и изменяя данные в классе данных D (только данные, без функций). Например, step1() создает d в D, step2() изменяет d, а step5() использует d.

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

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

Не совсем понятно, что происходит в вашем примере

Если вы создаете стиль производственной линии данных, у вас обычно будет что-то вроде этого, передавая по ссылке:
DataStruct d;
step1(d);
step2(d);
step3(d);
step4(d);
step5(d);

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

Таким образом, со схемой именования у вас может быть:

struct DataStruct {
    int S1Field1;
    int S1Field2;
    int S1Field3;
    int S2Field4;
    int S3Field5;
    (etc.)
}

... с префиксом, обозначающим этап, на котором создается это поле.

Если по какой-либо причине этого недостаточно, вы можете начать определять отдельные структуры:

struct DataStructBuild1 {
    int Field1; //new
    int Field2; //new
    int Field3; //new
}

struct DataStructBuild2 {
    int Field1; //from Build 1
    int Field2; //from Build 1
    int Field3; //from Build 1
    int Field4; //new
}
(etc.)

DataStructBuild1 d1;
DataStructBuild2 d2;
DataStructBuild3 d3;

step1(d1);
step2(d1, out d2);
step3(d2, out d3);

(etc.)

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

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

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

Я должен добавить одно последнее замечание: ответ @Christophe, связанный с ООП, был бы более подходящим, когда этап построения данных настолько велик или сложен, что он каким-то образом находился бы в компетенции нескольких групп программистов (или, по крайней мере, несколько человек, у которых есть отдельные области вызывает озабоченность), и должна быть система геттеров и сеттеров, определяющая интерфейс между сферами двух команд, исключения, чтобы гарантировать, что люди, которые не писали код и не знакомы с его внутренностями, были предупреждены, когда они не используют его должным образом, и так далее.

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


LetsCodeIt, 18 января 2023 г., 19:21