I'm writing a program that will interface with an external device. It will support numerous devices that may use different communication interfaces like USB, serial, etc.
This is what I have so far:
An abstract Device class with some generic methods and some pure virtual methods:
class Device
{
private:
bool initialised = false;
public:
bool isInitialised() const {
return this->initialised;
}
void setInitialised(bool initialised) {
this->initialised = initialised;
}
virtual void open() = 0;
virtual void close() = 0;
// These will be overridden by derived classes that implement a specific communication interface (USB, serial, etc)
virtual void read(unsigned char* buffer, int length, unsigned int timeout) = 0;
virtual void write(unsigned char* buffer, int length) = 0;
};
And numerous classes that extend the Device class and implement a specific communication interface. Here is a simplified version of the USBDevice class:
class USBDevice : public Device
{
private:
libusb_device* libUsbDevice = nullptr;
libusb_device_handle* libUsbDeviceHandle = nullptr;
uint16_t vendorId;
uint16_t productId;
public:
// Attempts to obtain a device handle and interface via libusb.
void open() override;
void close() override;
// Communication via libusb
void read(unsigned char* buffer, int length, unsigned int timeout) override;
// Communication via libusb
void write(unsigned char* buffer, int length) override;
};
Some of the devices employ their own communication protocol, and others (say, from a specific manufacturer) use the same protocol.
So, in order to avoid code duplication, I'd like to introduce a protocol layer, where I can define the protocol once and use it on multiple devices.
class Protocol1
{
};
class Protocol2
{
};
The problem is, I need to expose my device to my protocol class, so that I can communicate with the device via the protocol, like this:
Protocol1::doSomething()
{
// Send data to the device
device->write(blah blah);
}
And I'm not sure what the best way to do this would be.
I can have the Protocol classes inherit from the device class:
class Protocol1 : public Device
{
};
class Protocol2 : public Device
{
};
And then, for my actual device:
class SomeDevice : public USBDevice : public Protocol1
{
};
And for devices that support multiple protocols:
class SomeOtherDevice : public USBDevice : public Protocol1 : public Protocol2
{
};
The problem with this approach is that a) it results in a "Diamond of Dread" case, and b) it seems wrong in the sense that a protocol is not a type of device, so it shouldn't really inherit from the device class.
The other approach I thought of was to have the device compose of the protocol layer, because after all, a device does contain one or more protocols:
class SomeOtherDevice : public USBDevice
{
private:
Protocol1 protocol1;
Protocol2 protocol2;
};
And with this approach, I would expose the device to the protocol by injecting it at point of init:
Protocol1::Protocol1(Device dev)
{
this->device = dev;
}
But with this approach, it just feels hacky. I can't really think of a reason for why it feels like that, but it just seems wrong. I don't think it's as wrong as the first approach, where I'd effectively be abusing multiple inheritance, but I'm sure there's a better way that I haven't thought of.
Are any of the approaches I've described acceptable? How would you do it?
setInitialised
is a good idea – if at all, protected. – Aconcagua Mar 13 '19 at 3:09