By relying on a dictionary (e.g, std::unordered_map
or std::map
) that maps a command identifier (i.e., a std::string
object) to a Command
object, you can design a factory with dynamic registry for your Command
objects.
First, extend Command
by including another virtual member function, clone()
, that allows us to implement The Prototype Pattern:
class Command {
public:
// ...
virtual std::unique_ptr<Command> clone() const = 0;
};
The clone()
virtual member function does what its name suggests: it clones the object. That is, SpawnEnemyCommand
would override Command::clone()
in the following way:
class SpawnEnemyCommand : public Command {
public:
// ...
std::unique_ptr<Command> clone() const override {
// simply create a copy of itself
return std::make_unique<SpawnEnemyCommand>(*this);
}
};
This way, your command objects can be copied polymorphically through the Command
interface – i.e., you don’t need to know the concrete type of the command to be copied. All you need to do to copy a Command
object is to call its clone()
virtual member function. For example, the following function copies the Command
passed as an argument regardless of the underlying concrete type:
std::unique_ptr<Command> CopyCommand(const Command& cmd) {
return cmd.clone();
}
With this in mind, you are ready to design a factory for command objects, CommandFactory
, that supports dynamically registering your command objects:
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_;
};
It all boils down to a std::unordered_map<std::string, std::unique_ptr<Command>>
data member. Indexing this data member by a command identifier, which is an std::string
, we retrieve a Command
object – This is the prototype object we will use for cloning.
The registerCommand()
member function adds a Command
prototype to the registry:
void CommandFactory::registerCommand(std::string cmdId, std::unique_ptr<Command> cmd) {
registry_[cmdId] = std::move(cmd);
}
The createCommand()
member function clones the Command
prototype corresponding to the requested command identifier:
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();
}
As an example program:
auto main() -> int {
CommandFactory factory;
factory.registerCommand("spawn", std::make_unique<SpawnEnemyCommand>());
// ...
auto cmdPtr = factory.createCommand("spawn");
cmdPtr->execute();
}
You can also extend this factory to add support for dynamically deregistering your already-registered Command
prototypes.
CLICK HERE to find out more related problems solutions.