您问题的最佳解决方案主要取决于您的用例。
我看到两个主要用例:
- 您希望表示“设备”的“功能”,以便在从代码中操作该“设备”时获得可读的代码。例如具有播放、停止、暂停操作的 MediaPlayer 设备。但是您取消了简单地将成员函数添加到您的“设备”对象的选项,例如 Play(),因为您想将您的播放代码也重新用于另一个设备,例如调谐器设备。此外,您希望将操作应用于所有或这些“函数”的子集,例如 Enable()/Disable()/ToString()/Trigger(),Configure()...,这就是成员函数的原因方法不利。
- 您想要创建一个文档对象模型,该模型更注重数据。例如 Xml 文档。
根据您在问题中所写的内容,我假设您已经想到了用例 1。您的Object 类型具有您需要的所有常用操作。
然而,所有这些“功能”之间存在差异。
为了坚持您的简单方法,您需要手动设置/配置您的 Object 实例,从长远来看,这可能会很烦人,但很难避免:
// Example of "annoying": If you have more than 1 "device",
// you have to write such a function for each of them.
// Also, there is that fishy bit with Object.Id - the association between
// your configuration data and your object you want to configure.
void ConfigureMenu( std::vector& menuItems, MenuConfigurationData& configData )
{
for( auto& menuItem : menuItems )
{
menuItem.Configure( configData[menuItem.Id] ); // spoiler!
}
}
另外,我倾向于认为即使现在您的问题中也没有显示一些代码,这些代码配置了您的对象。
考虑到这一点,您可能希望摆脱手动为每个“设备”编写 1 个类类型的想法。下一个设备/菜单将需要同样对待,但需要更多专门的编码。
所以,我对你的建议是永远摆脱你的class Menu,抽象你的问题并为你的问题建模,就像:对象是你的“功能”,设备只是一组功能/对象.然后,您的 class Menu 就变成了一个名为 Menu 的实例。
typedef uint32_t FunctionId; // for example uint32_t...
typedef std::map<FunctionId,Object> Device; // aka. Menu.
然后,无论如何,在您最有可能拥有的配置函数中,您传入该 Device 映射的实例,并且您的配置函数用正确配置的 Object 填充它。
// those enums are still specific to your concrete device (here Menu) but
// you can consider the time it takes writing them an investment which will
// pay off later when you write your code, using your functions.
// You assign the function id which is used in your meta-data.
enum class MenuFunctions : FunctionId { play = ..., title = ..., instructions, ... };
// "generic" configuration function.
// Does not only configure your Object, but also puts it inside the device.
void ConfigureDevice( Device& device, ConfigData& configData )
{ // ...
}
稍后在您的代码中,您可以像这样访问函数:
menu[MenuFunctions::play].Trigger();
当然,还有其他方法和变体。例如,假设您有元数据(配置数据、设备描述),您可以停止手动编写所有这些代码,而是编写一些代码生成器来为您完成这项工作。
这种生成器的第一个版本可以为您创建配置函数和枚举。
有了所有这些,“嵌套类”的用例就变成了创建设备实例集合的问题。现在,由于您的所有设备都属于同一类型,您可以在闲暇时对它们进行组合和分组。