Factory Method Pattern in C++

Lokesh Bihani
7 min readApr 23, 2024

--

This is the second design pattern of my 8 part design patterns in C++ series. The first part was about Singleton Pattern. In this article, I’ll discuss about Factory Method Pattern, which is another creational design pattern. Creational patterns focus solely on creating objects.

I’ve explained this pattern using two examples: one using raw pointers and the other using smart pointers. If you’re not familiar with smart pointers, I encourage you to read this quick intro here before you reach the second example: Smart Pointers

Factory Method Pattern

The factory method pattern is a design pattern used in object-oriented programming to create objects without specifying the exact class of the object that will be created. It provides an interface for creating objects in a superclass but allows subclasses to alter the type of objects that will be created.

Let’s dissect this definition and understand what it actually means.

Create objects without specifying the exact class of the object that will be created

This means that before running the factory method, you don’t know which type of object you’re going to get after the method completes. There are a couple of observations to be made:

  • The type of the object is determined at runtime. This flexibility is crucial because if you knew the exact object type at compile time (during code compilation), you wouldn’t need the factory method. You could directly create the object of that specific type.
  • You need pointer or reference to a base class, and you also need a virtual factory method in the base class in order to call the correct instance methods of the object created by the factory method. This is Polymorphism.

The reason you need a pointer or a reference to a base class is because an object of derived class should be able to replace an object of a parent class. In other words, wherever an object of a parent (base) class is expected, you can use an object of its derived class(es). This is substitutability principle of inheritance (also known by Liskov Substitution principle). This allows for flexibility in usage.

It provides an interface for creating objects in a superclass but allows subclasses to alter the type of objects that will be created.

This means, an interface (in C++ it’s an abstract class) declares a method for creating objects, but the actual implementation of that method is left to the subclasses. This allows each subclass to decide the type of object it wants to create without modifying the client code.

Why it’s needed?

Imagine you have a situation where you want to create objects of different types based on certain conditions or parameters. Without the Factory Method pattern, you might end up with complex conditional statements in your code, which can become difficult to maintain and extend over time.

The problem isn’t necessarily with using if-else conditions per se. Rather, the issue arises from the need to understand how to create objects of the desired class without the assistance of the Factory Method pattern. Without this pattern, if an object needs to be created at multiple points in your code, you would be required to repeat the same instantiation logic each time. Furthermore, if the logic for object creation were to change (for example, if additional constructor arguments were required), you would need to update every instance where the object is instantiated.

The Factory Method pattern addresses these challenges by encapsulating the object creation logic within itself and providing an API or interface method that can be called to create objects. This approach minimizes redundancy and ensures that changes to the object creation process only need to be implemented in one place.

It’s worth noting that the effectiveness of the Factory Method pattern relies on maintaining consistency in the interface. If the interface were to change, it could potentially introduce complications and necessitate updates across the codebase.

When it is useful?

  • When you have a class that can have multiple subclasses, each providing a different implementation.
  • When you want to decouple the client code from the concrete classes being instantiated.
  • When you need to extend the application by adding new types of objects without modifying existing code.
UML Class Diagram of Factory Method Pattern

Let’s strengthen our understanding using some examples.

Logging

Let’s say you want to log messages in different ways depending on the context (e.g., to a file, to the console, over the network).

You could create a Logger interface and different implementations for each type of logging. The LoggerFactory would then produce the appropriate Logger based on the current context or configuration.

// AbstractProduct
class Logger {
public:
virtual void log(const string& message) = 0;
};

// ConcreteProductA
class FileLogger : public Logger {
public:
void log(const string& message) {
// Code to log message to a file
}
};

// ConcreteProductB
class ConsoleLogger : public Logger {
public:
void log(const string& message) {
// Code to log message to the console
}
};
// AbstractFactory
class LoggerFactory {
public:
virtual Logger* createLogger() = 0;
};

// ProductFactoryA
class FileLoggerFactory : public LoggerFactory {
public:
Logger* createLogger() {
return new FileLogger();
}
};

// ProductFactoryB
class ConsoleLoggerFactory : public LoggerFactory {
public:
Logger* createLogger() {
return new ConsoleLogger();
}
};
int main() {
LoggerFactory* factory = new FileLoggerFactory();
Logger* logger = factory->createLogger();
logger->log("This is a file log");

delete logger;
delete factory;

factory = new ConsoleLoggerFactory();
logger = factory->createLogger();
logger->log("This is a console log");

delete logger;
delete factory;
return 0;
}

Restaurant Order Processing

Let’s say you want to work on a restaurant ordering system, which allows customers to order two types of foods: Pizza and Pasta.

A FoodFactory could be used to create different types of food items (e.g., PizzaFactory, PastaFactory). Each factory would handle the specifics of creating a particular food item, including ingredients, preparation steps, and pricing calculations.

// AbstractFactory
class FoodFactory {
public:
virtual unique_ptr<FoodItem> createFood(FoodType type) const = 0;
};

// ProductFactoryA
class PizzaFactory : public FoodFactory {
public:
unique_ptr<FoodItem> createFood(FoodType type) const override {
if (type == PizzaType::CHEESE) {
return make_unique<CheesePizza>();
} else if (type == PizzaType::PEPPERONI) {
return make_unique<PepperoniPizza>();
} else {
return nullptr;
}
}
};

// ProductFactoryB
class PastaFactory : public FoodFactory {
public:
unique_ptr<FoodItem> createFood(FoodType type) const override {
if (type == PastaType::SPAGHETTI) {
return make_unique<Spaghetti>();
} else if (type == PastaType::LASAGNA) {
return make_unique<Lasagna>();
} else {
return nullptr;
}
}
};

I’m skipping AbstractProduct and ConcreteProduct classes. However, there’s an important point I want to discuss in this example.

Notice, I’ve used if-else conditions in both types of ProductFactory to create different types of Pizzas and Pastas. When you create objects using conditions like these, it’s called Simple Factory method . It’s the most simplest form of Factory method pattern but it violates Open/Closed principle, which states that every class should be open for extension but closed for modification.

Simple Factory vs. Factory Method:

  • Simple Factory: A single class (like PizzaFactory in the example) has a large conditional statement (or a series of if-else checks) that determines the concrete class to instantiate based on the input parameter (FoodType). It directly returns the created object.
  • Factory Method: An abstract factory class (FoodFactory) defines an interface (the createFood method) for object creation. Concrete factories (like PizzaFactory, PastaFactory) inherit from this abstract factory and implement the createFood method to handle specific object creation logic for their domain (pizzas in this case). This allows for more flexibility and potential for additional processing during object creation.

If I want to stick with Factory Method pattern, this is how I’d make updates:

class CheesePizzaFactory : public FoodFactory {
public:
unique_ptr<FoodItem> createFood() const override {
return make_unique<CheesePizza>();
}
};

class PepperoniPizzaFactory : public FoodFactory {
public:
unique_ptr<FoodItem> createFood() const override {
return make_unique<PepperoniPizza>();
}
};

int main() {
FoodFactory* factory = nullptr;
if (customerOrder.foodType == PizzaType::CHEESE) {
factory = new CheesePizzaFactory();
} else if (customerOrder.foodType == PizzaType::PEPPERONI) {
factory = new PepperoniPizzaFactory();
} else {
// Handle invalid pizza type
}

unique_ptr<FoodItem> food = factory->createFood(customerOrder.foodType);
}

This implentation is better than the previous one but some might argue this also violates open/closed principle. So, a better way to fix this is by creating a registry factory. This is how it looks:

// This is singleton class.
class FactoryRegistry {
public:
static FactoryRegistry& getInstance() {
static FactoryRegistry instance;
return instance;
}

void registerFactory(FoodType type, unique_ptr<FoodFactory> factory) {
factories[type] = move(factory);
}

unique_ptr<FoodFactory> getFactory(FoodType type) const {
auto it = factories.find(type);
if (it != factories.end()) {
return unique_ptr<FoodFactory>(it->second->clone());
}
return nullptr;
}

private:
FactoryRegistry() = default;
unordered_map<FoodType, unique_ptr<FoodFactory>> factories;
};

int main() {
FactoryRegistry& registry = FactoryRegistry::getInstance();

registry.registerFactory(FoodType::PIZZA, make_unique<CheesePizzaFactory>());
registry.registerFactory(FoodType::PASTA, make_unique<SpaghettiFactory>());

FoodType customerOrder = FoodType::PIZZA;

unique_ptr<FoodFactory> factory = registry.getFactory(customerOrder);

return 0;
}

The FactoryRegistry class manages the registration and retrieval of factory instances based on the food type. Factories are registered with the registry during initialization and the main function retrieves the appropriate factory from the registry based on the customer order and creates the food item using the factory.

This new implementation adheres with the open/closed principle.

If you need to add a new type of pizza (e.g., VeggiePizza), you only need to create a new VeggiePizzaFactory class without modifying the existing code that uses the factory method. This promotes code flexibility.

You probably might have noticed that I implemented polymorphism using the pointer of the base class rather the reference of the base class. There are a few reason for that:

  1. Pointers can represent a null or empty state (nullptr), whereas references cannot. So, if you’re sure that returned object will always be valid, then reference would be fine otherwise, using pointers is a safer option.
  2. Using pointers for polymorphism, it’s possible to use the same pointer to later point to a different object of its derived classes but with reference it’s not possible. Once object is assigned to a reference variable it’s not possible for reference to later point to a different object. I usually prefer using references in function arguments.

Summary

  • It provides a common interface in a base class for creating objects, but lets subclasses decide the actual type of objects to create.
  • It is useful when a class doesn’t know beforehand what kind of objects it needs to create, or the object creation logic is complex.
  • Code becomes more adaptable and easier to modify as new requirements emerge, since the responsibility of object creation is shifted to subclasses.
  • It promotes loose coupling between classes, making the code more maintainable and extensible.

--

--

Lokesh Bihani

Software Engineer passionate about System Design, DevOps, and ML. I try to simplify complex tech concepts to help others learn while deepening my own knowledge.