我有大量的(〜100)类是从一个通用基类(Device
)派生的。每个设备都可以接受大量类似命令的某些子集。不同的命令可以具有不同数量和类型的参数,因此每个命令都按其自己的类型封装(如有必要,可以更改)。
在仅给Device
基类提供指针/引用的情况下,有什么模式可以使我将命令传递给设备,以便设备可以访问命令的类型和参数?
我提出的选项:
Device
中添加一个接受每种命令类型的单独虚拟方法。但是,这最终将在基类中使用大量虚方法,而这些虚方法仅在很少的派生类中被覆盖。 对于在
Device
基类中没有大量虚拟方法的情况下,可以干净利落地使用此模式的任何建议?
最佳答案
这是经典的double dispatch problem。
我已经遇到过这种模式几次,并使用以下策略来处理它。
假设基类Command
具有一个返回“id”的函数,该ID可以是整数类型,字符串类型,也可以用作映射中的键。
struct Command
{
typedef <SomeType> IDType;
virtual IDType getID() const = 0;
};
Device
的接口(interface)可以简化为:struct Command;
struct Device
{
virtual execute(Command const& command) = 0;
};
假设
DeviceABCD
是派生类型之一,并且通过基类指针/引用操作的实际设备是DeviceABCD
。在第一次调度中,将执行命令的调用调度到DeviceABCD::execute()
。DeviceABCD::execute()
的实现将其分派(dispatch)给另一个执行实际工作的函数。您需要一个适当的框架来正确执行第二次调度。在框架中:
基于这些,您可以在给定“命令ID”的情况下获得“命令执行器”。如果存在“命令执行器”,则可以简单地将命令执行分派(dispatch)到“命令执行器”。如果不是,则需要处理错误,最有可能通过引发异常来处理。
该框架可用于
Device
的所有子类型。因此,可以使用Device
本身或与Device
对等的帮助程序类来实现该框架。我更喜欢第二种方法,建议创建几个类:CommandExecutor
和CommandDispatcher
。CommandExecutor.h:
struct CommandExecutor
{
virtual execute(Command const& command) = 0;
};
CommandDispatcher.h:
class CommandDispatcher
{
public:
void registerCommandExecutor(Command::IDType commandID,
CommandExecutor* executor);
void executeCommand(Command const& command);
std::map<Command::IDType, CommandExecutor*>& getCommandExecutorMap();
public:
std::map<Command::IDType, CommandExecutor*> theMap;
};
CommandDispatcher.cpp:
void CommandDispatcher::registerCommandExecutor(Command::IDType commandID,
CommandExecutor* executor)
{
getCommandExecutorMap()[commandID] = executor;
}
void CommandDispatcher::executeCommand(Command const& command)
{
CommandExecutor* executor = getCommandExecutorMap()[commandID];
if ( executor != nullptr )
{
executor->execute(command);
}
else
{
throw <AnAppropriateExecption>;
}
}
std::map<Command::IDType, CommandExecutor*>& CommandDispatcher::getCommandExecutorMap()
{
return theMap;
}
如果
DeviceABCD
可以执行Command12
和Command34
,则其实现将类似于:DeviceABCD.cpp:
struct Command12Executor : public CommandExecutor
{
virtual void execute(Command const& command) { ... }
};
struct Command34Executor : public CommandExecutor
{
virtual void execute(Command const& command) { ... }
};
DeviceABCD::DeviceABCD() : commandDispatcher_(CommandExecutor)
{
static Command12Executor executor12;
static Command34Executor executor34;
// This assumes that you can get an ID for all instances of Command12
// without an instance of the class, i.e. it is static data of the class.
commandDispatcher_.registerExecutor(Command12Type, &executor12);
commandDispatcher_.registerExecutor(Command34Type, &executor34);
}
有了该框架,
DeviceABCD::execute()
的实现就非常简单。void DeviceABCD::execute(Command const& command)
{
commandDispatcher_.executeCommand(command);
}
精简到可以在基类中实现的程度。仅当需要在命令被分派(dispatch)到正确的CommandExecutor之前对命令进行按摩或更新某些其他状态时,才需要在派生类中实现它。
关于c++ - 寻找设计模式以减少虚拟方法的重载,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33613155/