c++ - 在运行时创建 Command 对象
问题描述
我正在尝试为游戏引擎编写一个控制台,它允许我输入命令来执行任务,例如更改地图、生成敌人等。
我一直在尝试使用命令模式来做到这一点(按照 gameprogrammingpatterns.com 的示例)。有关我当前代码结构的概述,请参见下文。
parseCommand
处理string
来自用户的,提取命令名称和参数(当前仅使用空格分隔)。下一步是我卡住的地方。我需要创建一个Command*
可以调用execute
的方法,但我只有string
命令的名称。
我的函数中可能有一大堆if
语句parseCommand
,例如:
if (cmdName == "spawn")
return new SpawnEnemyCommand();
或者,我可以在我的类中存储一个指向每个命令的指针Console
,例如Command *spawnNewEnemy = new SpawnNewEnemy();
然后在parseCommand
do中if (cmdName == "spawn") spawnNewEnemy->execute();
。第二个选项似乎是 gameprogrammingpatterns book 是如何做到的。
如果我最终得到数百个控制台命令,这些选项似乎都不是很实用。我已经研究了我能找到的关于这种模式的所有文章和帖子,但这并没有帮助我澄清情况。
如何Command
从内部干净地实例化正确的对象parseCommand
?
命令接口基类:
class Command {
public:
virtual ~Command() { }
virtual void execute() = 0;
};
示例接口实现:
class SpawnEnemyCommand : public Command {
public:
void execute() {
// method calls to perform command go here
}
};
控制台类头:
class Console {
public:
Command* parseCommand(std::string);
bool validateCommand(std::string, std::vector<std::string>);
};
解决方案
通过依赖将命令标识符(即对象)映射到对象的字典(例如,std::unordered_map
或) ,您可以为您的对象设计一个具有动态注册表的工厂。std::map
std::string
Command
Command
首先,Command
通过包含另一个虚拟成员函数来扩展clone()
,它允许我们实现原型模式:
class Command {
public:
// ...
virtual std::unique_ptr<Command> clone() const = 0;
};
虚成员函数的clone()
作用正如其名:它克隆对象。也就是说,将通过以下方式SpawnEnemyCommand
覆盖:Command::clone()
class SpawnEnemyCommand : public Command {
public:
// ...
std::unique_ptr<Command> clone() const override {
// simply create a copy of itself
return std::make_unique<SpawnEnemyCommand>(*this);
}
};
这样,您的命令对象可以通过接口多态Command
地复制——也就是说,您不需要知道要复制的命令的具体类型。复制对象所需要做的Command
就是调用它的clone()
虚拟成员函数。例如,以下函数将Command
传递的作为参数复制,而不管底层的具体类型如何:
std::unique_ptr<Command> CopyCommand(const Command& cmd) {
return cmd.clone();
}
考虑到这一点,您已经准备好为命令对象设计一个工厂CommandFactory
,它支持动态注册您的命令对象:
class CommandFactory {
public:
void registerCommand(std::string id, std::unique_ptr<Command>);
std::unique_ptr<Command> createCommand(std::string id) const;
private:
std::unordered_map<std::string, std::unique_ptr<Command>> registry_;
};
这一切都归结为一个std::unordered_map<std::string, std::unique_ptr<Command>>
数据成员。通过命令标识符(即 an )索引此数据成员std::string
,我们检索一个Command
对象——这是我们将用于克隆的原型对象。
registerCommand()
成员函数将原型Command
添加到注册表:
void CommandFactory::registerCommand(std::string cmdId, std::unique_ptr<Command> cmd) {
registry_[cmdId] = std::move(cmd);
}
createCommand()
成员函数克隆Command
对应于请求的命令标识符的原型:
std::unique_ptr<Command> CommandFactory::createCommand(std::string cmdId) const {
auto const it = registry_.find(cmdId);
if (it == registry_.end())
return nullptr; // no such a command in the registry
auto const& cmdPtr = it->second;
return cmdPtr->clone();
}
作为示例程序:
auto main() -> int {
CommandFactory factory;
factory.registerCommand("spawn", std::make_unique<SpawnEnemyCommand>());
// ...
auto cmdPtr = factory.createCommand("spawn");
cmdPtr->execute();
}
您还可以扩展此工厂以添加对动态取消注册已注册Command
原型的支持。
推荐阅读
- javascript - 如何在angularjs中的html之间传递变量?
- python - 如果“id”出现少于 2 次,则删除 pandas 数据框中的行
- angular - 在 primeNG 焦点上未触发 completeMethod 事件
- r - SASxport 到 R:读取 XPT SAS 文件时出错
- java - 如何提高算法的运行时间?
- git - 使用过滤器干净地列出所有远程分支
- asp.net-core - 如何在 ASP.NET Core 应用程序中使用领域集合通知?
- java - RecyclerView 列表项不显示也不播放
- python - 我的连接超时的原因是什么?
- python - 在 Google Colab 中保存失败