您正在寻找的是 OOP 中的常见模式。 设计模式 (四人合一书)将此称为命令模式。
通常不需要模板。一切都在运行时解析和分派,因此动态多态性(虚拟函数)可能是更好的选择。
在另一个答案中,Rafael Baptista 提出了基本设计。以下是我将如何修改他的设计以使其更完整:
命令对象和 CommandDispatcher
命令由类的子类处理Command
。CommandDispatcher
命令由处理命令字符串的基本解析(基本上,在空格处拆分,可能处理带引号的字符串等)的对象调度。
系统使用 注册一个实例Command
,CommandDispatcher
并将每个实例Command
与一个命令名称 ( std::string
) 相关联。关联由一个std::map
对象处理,尽管它可以被一个哈希表(或类似的结构来关联键值对)替换。
class Command
{
public:
virtual ~Command(void);
virtual void execute(FILE* in, const std::vector<std::string>& args) = 0;
};
class CommandDispatcher
{
public:
typedef std::map<std::string, Command*> CommandMap;
void registerCommand(const std::string& commandName, Command* command)
{
CommandMap::const_iterator cmdPair = registeredCommands.find(commandName);
if (cmdPair != registeredCommands.end())
{
// handle error: command already registered
}
else
{
registeredCommands[commandName] = command;
}
}
// possibly include isRegistered, unregisterCommand, etc.
void run(FILE* in, const std::string& unparsedCommandLine); // parse arguments, call command
void dispatch(FILE* in, const std::vector<std::string>& args)
{
if (! args.empty())
{
CommandMap::const_iterator cmdPair = registeredCommands.find(args[0]);
if (cmdPair == registeredCommands.end())
{
// handle error: command not found
}
else
{
Command* cmd = cmdPair->second;
cmd->execute(in, args);
}
}
}
private:
CommandMap registeredCommands;
};
我已经省略了解析和其他细节,但这是命令模式的一种非常常见的结构。请注意std::map
句柄如何将命令名称与命令对象相关联。
注册命令
要使用这种设计,您需要在系统中注册命令。您需要在或另一个中心位置CommandDispatcher
使用单例模式实例化 。main
然后,您需要注册命令对象。有几种方法可以做到这一点。我更喜欢的方式是让每个模块(相关命令集)提供自己的注册功能,因为您拥有更多控制权。例如,如果您有一个“文件 IO”模块,那么您可能有一个函数fileio_register_commands
:
void fileio_register_commands(CommandDispatcher* dispatcher)
{
dispatcher->registerCommand( "readfile", new ReadFileCommand );
dispatcher->registerCommand( "writefile", new WriteFileCommand );
// etc.
}
这里ReadFileCommand
和是实现所需行为WriteFileCommand
的子类。Command
您必须确保fileio_register_commands
在命令可用之前调用。
这种方法可以用于动态加载的库(DLL 或共享库)。确保注册命令的函数具有基于模块名称的常规模式:XXX_register_commands
,XXX
例如,小写的模块名称。加载共享库或 DLL 后,您的代码可以确定是否存在这样的函数,然后调用它。