Let me tell you the problem I have. I'm designing a set of classes to control a digital device. This device can work in two modes of operation. In the first mode it can perform a specific set of operations, and in the second mode it can perform another set of operations (with possibly some common operations between the two). I can also change the mode of the device on the run, so I can swap between the two modes if necessary. Independently of the mode, the device use the same set of registers.
I was thinking in solve this problem with one base class for each mode, so I can have objects of mode 1 when I need the first set of operations and objects of mode 2 when I need the second set of operations. Then I could derive a class from these two base classes, so I can have objects that perform all the operations.
The problem with my design is that the two base classes have some common functions and references to the same registers. Since I can't prevent inheritance of members I would have duplicates in the derived class. I know I can choose which duplicate to access with the scope operator, but I still think this a bad design.
So my question is: is there an idiomatic way of solve this problem?
If there isn't a right or easy way of solving this, I'm thinking about design 3 hierarchically independently classes. I would have some duplicate code, but that is not a big problem, right?
Code below (simplified) for illustration:
class mode1
{
protected:
volatile uint8_t& reg1;
volatile uint8_t& reg2;
uint8_t data;
public:
virtual void operation1() final { // do something }
virtual void operation2() final { // do something }
virtual void operation3() final { // do something }
};
class mode2
{
protected:
volatile uint8_t& reg1;
volatile uint8_t& reg2;
uint8_t data;
public:
virtual void operation4() final { // do something }
virtual void operation2() final { // do something }
virtual void operation5() final { // do something }
};
class mode1and2 : public mode1, public mode2
{
public:
void operation6() { // do something }
void operation7() { // do something }
};
Note modes 1 and 2 have operation2 and all the data members in common.
I'd put the common parts of mode1
and mode2
in a common base class, let's say Common
, comprising then your data and member function operation2
. Then, together with virtual inheritance, you can have two views on the same data, even at the same time if needed.
class common {
friend class mode1;
friend class mode2;
protected:
volatile uint8_t& reg1;
volatile uint8_t& reg2;
uint8_t data;
public:
virtual void operation2() final { // do something
};
};
class mode1 : public virtual common
{
public:
virtual void operation1() final { // do something
};
virtual void operation3() final { // do something }
};
};
class mode2 : public virtual common
{
public:
virtual void operation4() final { // do something
}
virtual void operation5() final { // do something
}
};
class mode1and2 : public mode1, public mode2
{
public:
void operation6() { // do something }
};
void operation7() { // do something }
};
};