背景:
目标:
能够通过指针访问函数和变量,而不必不断转换void指针。
在调用函数之前,我不应该知道我使用的是什么类型的设备吗?
嗯,是的,我想,但让我们考虑以下几点。
void * _type;
uint8_t deviceType = getDeviceTypeFromEEPROM();
//somewhere at the beginning
switch(deviceType)
{
case DEVICE_A:
_type = new DeviceA();
break;
case DEVICE_B:
_type = new DeviceB();
break;
}
//somewhere during execution.
//I want to avoid having to do something like this. 2 types in example, in reality I have over 10.
//I would prefer not to put a switch and cast every time I need flexibility.
switch(deviceType)
{
case DEVICE_A:
static_cast<DeviceA*>(_type)->readEEPROM();
break;
case DEVICE_B:
static_cast<DeviceB*>(_type)->readEEPROM();
break;
}
设想的解决方案:
有一个基类并使用虚函数。变量呢?我是否需要有虚函数来访问驻留在不同类中的变量?
其他解决方案:
对任何其他解决方案都是开放的。
下面是我如何实现OOP风格解决方案的草图;没有什么特别的,只是程序支持的不同类型参数的枚举,以及idevice
接口,该接口表示知道如何设置或获取这些类型的值的任何设备。然后,您可以根据需要为具有特定于该设备类型的参数的设备实现子类:
enum class ParameterType {
BatteryLevel,
Uptime,
SerialNumber,
DeviceAProprietarySetting,
DeviceBProprietarySetting,
// [... add more values here as necessary...]
};
typedef std::variant<int, float, std::string> ParameterValue;
// Abstract interface to any device
// (so you can pass around IDevice* pointers instead of void* pointers)
class IDevice
{
public:
IDevice() {}
virtual ~IDevice() {}
/** Subclass should implement this to write the specified parameter-value
* into (retValue) and return true... or if it cannot (e.g. because it
* doesn't support that particular parameter-value), it should return
* false to indicate failure.
*/
virtual bool GetValue(ParameterType pt, ParameterValue & retValue) const = 0;
/** Subclass should implement this to accept the specified parameter-value
* from (newValue) and return true... or if it cannot (e.g. because it
* doesn't support that particular parameter-value), it should return
* false to indicate failure to set anything.
*/
virtual bool SetValue(ParameterType pt, const ParameterValue & newValue) = 0;
// more virtual methods could be added here, if there are other actions
// that most of your devices support
};
// Since most devices have these parameters, we'll move them up into a shared
// base class to avoid having to reimplement that functionality for every device
class TypicalDevice : public IDevice
{
TypicalDevice() {}
virtual bool GetValue(ParameterType pt, ParameterValue & retValue) const
{
switch(pt)
{
case ParameterType::BatteryLevel: retValue = _batteryLevel; return true;
case ParameterType::Uptime: retValue = _uptime; return true;
case ParameterType::SerialNumber: retValue = _serialNumber; return true;
default: return false; // ParameterType not found!
}
}
virtual bool SetValue(ParameterType pt, const ParameterValue & newValue)
{
switch(pt)
{
case ParameterType::BatteryLevel: _batteryLevel = std::get<float>(newValue); return true;
case ParameterType::Uptime: _uptime; = std::get<int>(newValue) return true;
case ParameterType::SerialNumber: _serialNumber = std::get<std::string>(newValue); return true;
default: return false; // ParameterType not found!
}
}
private:
float _batteryLevel;
int _uptime; // in seconds?
std::string _serialNumber;
};
// Implementation for some specific device-type
class DeviceA : public TypicalDevice
{
DeviceA() {}
virtual bool GetValue(ParameterType pt, std::variant & retValue) const
{
switch(pt)
{
case ParameterType::DeviceAProprietarySetting: retValue = _deviceAProprietarySetting; return true;
default:
return TypicalDevice::GetValue(pt, retValue);
}
}
virtual bool SetValue(ParameterType pt, const std::variant & newValue)
{
switch(pt)
{
case ParameterType::DeviceAProprietarySetting: _deviceAProprietarySetting = std::get<float>(newValue); return true;
default:
return TypicalDevice::GetValue(pt, retValue);
}
}
private:
float _deviceAProprietarySetting;
};