There is, more often than not, a need to share same data between different objects/classes. I am aware of a few different ways of doing this:
- Global variables: Loathed by most and for the right reasons.
- Singletons: This provides limited control on who can or cannot modify the data, at least for the problem discussed below.
- Dependency Injection: Same problem as singletons.
Sometimes, this data sharing gives rise to design with weak encapsulation of this shared data. Two common situations, that arise quite often, are as follows:
States implementing a state interface in a state machine need access to the same set of data.
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;
};
Here, concrete implementations such as ConcreteStateA
will need access to SharedData
to e.g. get/set certain data, may be use this information to decide on state transition etc. As in the example above, we could declare SharedData
as a class and provide accessors/mutators. Or we could simply declare SharedData
as a struct. However, in both cases, the concrete implementations will be able to modify any parameter within SharedData
. For example, let's say that ConcreteStateA
has nothing to do with flag
and hence should not be able to mutate it. With the given interface, however, we cannot control that behavior. Both ConcreteStateA
and ConcreteStateB
have access to all the data and can get/set any parameter. Is there a better design/solution for this problem? One that offers more protection to the shared data. Or, can we somehow enforce a constraint that a certain ConcreteState
is able to modify only certain parameters of the SharedData
while still implementing the common StateInterface
?
Sub-routines of a giant algorithm implementing a sub-routine interface need access to the same set of data.
class SubroutineInterface
{
public:
virutal void DoSubroutine(SharedData* in_shared_data) = 0;
}
class ConcreteSubroutine : public SubroutineInterface
{
public:
virutal void DoSubroutine(SharedData* in_shared_data) final;
};
Same questions as in the state machine example...