C ++ обмен данными между объектами / классами, реализующими один и тот же интерфейс

Чаще всего существует необходимость в совместном использовании одних и тех же данных между различными объектами / классами. Я знаю несколько разных способов сделать это:

  1. Глобальные переменные: ненавидят большинство и по правильным причинам.
  2. Singletons: это обеспечивает ограниченный контроль над тем, кто может или не может
    измените данные, по крайней мере, для проблемы, обсуждаемой ниже.
  3. Внедрение зависимостей: та же проблема, что и у одиночек.

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

Состояния, реализующие интерфейс состояний в автомате состояний, нуждаются в доступе к одному и тому же набору данных.

class SharedData
{
public:
double GetVar() const {return var;}
bool GetFlag() const {return flag;}
void SetVar(double in_var) {var = in_var;}
void SetFlag(bool in_flag) {flag = in_flag;}

private:
double var;
bool flag;
};

class StateIface
{
public:
virtual void Run(SharedData* in_shared_data) = 0;
};

class ConcreteStateA : public StateIface
{
virtual void Run(SharedData* in_shared_data) final;
};

class ConcreteStateB : public StateIface
{
virtual void Run(SharedData* in_shared_data) final;
};

Здесь конкретные реализации, такие как ConcreteStateA понадобится доступ к SharedData например получить / установить определенные данные, возможно, использовать эту информацию для принятия решения о переходе состояния и т. д. Как и в примере выше, мы могли бы объявить SharedData как класс и предоставить аксессоры / мутаторы. Или мы могли бы просто объявить SharedData как структура. Однако в обоих случаях конкретные реализации смогут изменять любой параметр в пределах SharedData, Например, скажем, что ConcreteStateA не имеет ничего общего с flag и, следовательно, не должны быть в состоянии изменить его. Однако с данным интерфейсом мы не можем контролировать это поведение. И то и другое ConcreteStateA а также ConcreteStateB иметь доступ ко всем данным и может получить / установить любой параметр. Есть ли лучший дизайн / решение для этой проблемы? Тот, который предлагает больше защиты для общих данных. Или мы можем как-то применить ограничение, которое определенное ConcreteState может изменять только определенные параметры SharedData все еще реализуя общее StateInterface?

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

class SubroutineInterface
{
public:
virutal void DoSubroutine(SharedData* in_shared_data) = 0;
}

class ConcreteSubroutine : public SubroutineInterface
{
public:
virutal void DoSubroutine(SharedData* in_shared_data) final;
};

Те же вопросы, что и в примере с конечным автоматом …

2

Решение

Вы можете добиться дополнительной защиты, сделав SharedData непрозрачный тип

library.h

class SharedData;

void foo(SharedData*);

class bar {
public:
void method(SharedData*);
};

library_private.h

class SharedData {
public:
int x;
};

library.cpp

#include "library.h"#include "library_private.h"
void foo(SharedData* d) {
d->x = 0;
}

void bar::method(SharedData* d) {
d->x = 1;
}

Таким образом, только файлы cpp, включая library_private.h, имеют доступ к SharedDataинтерфейс, но SharedData экземпляры все еще могут быть переданы вокруг остальной части проекта.

Единственная сложная часть — это управление временем жизни SharedData, так как вам придется выделить SharedData большую часть времени, и умные указатели должны иметь прикрепленные к ним пользовательские удалители. Таким образом, непрозрачные типы часто оборачиваются в некоторый тип оболочки для управления RAII.

Что-то вроде этого:

в заголовке:

class SharedDataImpl;
class SharedData {
public:
SharedData();
~SharedData();

SharedDataImpl* get() {
return impl_.get();
}
private:
std::unique_ptr<SharedDataImpl> impl_;
};

в .cpp:

SharedData()
: impl_(std::make_unique<SharedDataImpl>()) {}

~SharedData() {}
2

Другие решения

Вы можете использовать идиому PassKey, что-то вроде:

class SharedData
{
public:
class FlagKey
{
friend class ConcreteStateA;
// List here classes which can modify Flag
private:
FlagKey() {}
FlagKey(const FlagKey&) = delete;
};

class VarKey
{
friend class ConcreteStateB;
// List here classes which can modify Var
private:
VarKey() {}
VarKey(const FlagKey&) = delete;
};

public:
double GetVar() const {return var;}
bool GetFlag() const {return flag;}
void SetVar(VarKey, double in_var) {var = in_var;}
void SetFlag(FlagKey, bool in_flag) {flag = in_flag;}
private:
double var = 0;
bool flag = false;
};

А потом:

class ConcreteStateA : public StateIface
{
public:
virtual void Run(SharedData& data) final {
// data.SetVar({}, 0); // error: calling a private constructor of class 'VarKey'
data.SetFlag({}, false);
}
};
2

По вопросам рекламы [email protected]