creating command objects at run-time

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.

Leave a Comment

Your email address will not be published.

Scroll to Top