I'll expand a bit on your design by introducing an adapter.
Implementation
Firstly, you have somewhere an implementation. This could be a library from the internet that you don't want or can't change. Lets say this looks like this:
class Relay
{
public:
void Set(bool value);
};
Interface
Now in our application we have defined an interface for working with relays. So there is one single way to work with relays throughout the application.
class IRelay
{
public:
virtual void TurnOn() = 0;
virtual void TurnOff() = 0;
};
Adapter
As you can see, the interface is different from the relay implementation. We can't change the implementation because this was a library from the internet. And who knows, we may get another relay in the future that has different methods. (Relay may not be the best example. But what about an IC that measures current?)
Also, we don't want to change the interface, because this would require us to change code all over the place. Besides, if we support 2 different relays with different implementations this isn't possible at all.
This is where the adapter comes in. It forms a link between the library and the interface. Now every time we need to support another relay. We just make another adapter and the relay can be used everywhere in our code.
class RelayAdapter : IRelay
{
Relay& relay;
public:
RelayAdapter(Relay& relay)
: relay(relay)
{
}
void TurnOn() override {
relay.Set(true);
}
void TurnOff()override {
relay.Set(false);
}
};
MyDevice
So, how would this look in your code? First, we need to instantiate the relay and the adapter, so it can be used.
class MyDevice
{
Relay actualRelay1;
Relay actualRelay2;
RelayAdapter adapter1 {actualRelay1};
RelayAdapter adapter2 {actualRelay2};
public:
IRelay relay1 = adapter1;
IRelay relay2 = adapter2;
}
This class can be used:
int main()
{
MyDevice device;
device.relay1.TurnOn();
device.relay2.TurnOff();
return 0;
}
Btw, like G. Sliepen mentioned, keep overhead in mind! For us the overhead is acceptable especially when keeping the advantages in mind. For the ISR, this is probably not preferred. I would do something with an semaphore and a different task to handle isr. Ofcourse, this is dependant on your situation.