C++ Factory Class

Published in category Programming
on Christian Mayer's Weblog.

As I wrote in my previous blog post, I’m about to rewrite my Ruby Wallet project in C++17. See WalletCpp on my GitHub profile.

I want to use best practice and the latest standard C++17. So no raw pointers anymore. You should get rid of your raw pointers in your C++ code. You can do almost everything the same way using smart pointers.

Let’s assume you want to create a Command Factory to produce Command objects from a given name. You need a base class that every command class will derivative from.

Let’s dive straight into the code.

class Command; // Base class

class AddCommand : public Command {};
class EditCommand : public Command {};
class DeleteCommand : public Command {};

In this example above we have 3 commands: Add, Edit and Delete.

We will be able to get a new Command object for a specific name. For the purpose of this post I assume that the header file is already guarded.

// command_factory.hpp
// already guarded

#include <string>      // for std::string
#include <map>         // for std::map
#include <functional>  // for std::function

class CommandFactory final
{
public:
  static void setup() noexcept;
  std::unique_ptr<Command> makeCommand(const std::string&) const;

private:
  static bool isSetup;
  static std::map<std::string, std::function<std::unique_ptr<Command>()>> creators;
  };
};

Here the corresponding implementation file:

// command_factory.cpp

void CommandFactory::setup() noexcept
{
  if (isSetup) {
    // Run setup only once.
    return;
  }
  isSetup = true;

  // Clear creators.
  creators.clear();

  creators["add"] = []()->std::unique_ptr<Command> {
    return std::make_unique<AddCommand>();
  };
  creators["edit"] = []()->std::unique_ptr<Command> {
    return std::make_unique<EditCommand>();
  };
  creators["delete"] = []()->std::unique_ptr<Command> {
    return std::make_unique<DeleteCommand>();
  };
}

std::unique_ptr<Command> CommandFactory::makeCommand(const std::string& _name) const
{
  auto fn = creators[_name];
  if (fn == nullptr) {
    throw (std::string{"Command not found: "} + _name);
  }
  return fn(); // Call lambda and return new unique pointer.
}

// Private
bool CommandFactory::isSetup = false;
std::map<std::string, std::function<std::unique_ptr<Command>()>>
  CommandFactory::creators;

The setup() member function will only add lambda functions to a <string,function> map. At this point we do not create new objects. Only lambda function. New objects will be created when makeCommand() is called.

You can now we use it. First, setup the factory:

CommandFactory::setup();
CommandFactory factory;

Then new commands can be created using a name:

auto addCommand = factory.makeCommand(std::string{"add"});
auto editCommand = factory.makeCommand(std::string{"edit"});

In this example we use a fixed string to create a new command. A more realistic example:

auto command = factory.makeCommand(commandName);

Where commandName has a dynamic value and you do not know which object command holds. See how I used a Factory Class in my WalletCpp project.

Recent Posts

About the Author

Christian is a professional software developer living in Vienna, Austria. He loves coffee and is strongly addicted to music. In his spare time he writes open source software. He is known for developing automatic data processing systems for Debian Linux.