Hello! Welcome to your next lesson on C++ design patterns.
In our previous lesson, we explored the Singleton pattern, a creational pattern designed to restrict object creation to a single instance. We focused on making it thread-safe using std::call_once and saw how modern C++ provides an elegant solution with function-local static variables.
Today, we'll investigate another powerful creational pattern: the Factory Method. Where the Singleton restricts, the Factory Method provides flexibility. Its goal is to define an interface for creating an object but let subclasses decide which class to instantiate. This pattern is fundamental to building extensible and maintainable systems, as it decouples your high-level logic from the concrete types it needs to work with.
Our learning outcome is to implement the Factory Method pattern using polymorphism to allow subclasses to decide which class to instantiate. We'll start with the core problem it solves, look at its implementation, dive into the polymorphic mechanics that make it work, and finally, refactor it using modern C++ best practices for memory safety.
1. The Problem: Tight Coupling
Imagine you're building an application that needs to create different types of objects based on some input. A classic example is a document editor that can create different types of documents (.txt, .docx, .pdf). A naive implementation might look like this:
// Client code that needs to create a document
std::string userChoice = "docx";
Document* doc = nullptr;
if (userChoice == "txt") {
doc = new TextDocument();
} else if (userChoice == "docx") {
doc = new WordDocument();
} else if (userChoice == "pdf") {
doc = new PdfDocument();
}
// ... use the doc ...
delete doc;
This code has a significant problem: the client code is tightly coupled to the concrete TextDocument, WordDocument, and PdfDocument classes. Every time you want to add a new document type (e.g., MarkdownDocument), you have to find and modify every if/else block like this in your application. This violates the Open/Closed Principle, which states that software entities should be open for extension but closed for modification.
2. A First Step: The Simple Factory Idiom
A common first step to improve this is to encapsulate the creation logic into a single function, often called a "Simple Factory."
Design Patterns - Factory Method Pattern Explanation and Implementation in C++
Let's watch a segment from a video by Mike Shah that clearly illustrates this initial refactoring. He uses a game object example, but the principle is identical. Pay attention to how he moves the object creation logic out of the main client code and into a dedicated function.
Watch the sections from 03:34 to 14:10. In the first part (until 10:26), he builds a basic factory function that takes a string and returns a raw pointer. In the second part (from 10:26), he improves it by using an enum class instead of a string, which is better for type safety.
As you saw, the code is now structured like this:
// Product interface
class IGameObject {
public:
virtual ~IGameObject() = default;
// ... other virtual functions
};
// Concrete products
class Plane : public IGameObject { /* ... */ };
class Boat : public IGameObject { /* ... */ };
// Enum for types
enum class ObjectType { Plane, Boat };
// Simple Factory function
IGameObject* makeGameObject(ObjectType type) {
if (type == ObjectType::Plane) {
return new Plane();
} else if (type == ObjectType::Boat) {
return new Boat();
}
return nullptr;
}
This is an improvement. The client code is now decoupled from the new expressions. If we add a Submarine class, we only need to modify the makeGameObject function. However, this still concentrates all creation logic in one place, which can become unwieldy. The true Factory Method pattern takes this one step further by using polymorphism.
3. The Factory Method Pattern
The Factory Method pattern lets subclasses handle the decision of which object to create. This is achieved by defining a virtual "factory method" in a base Creator class, which ConcreteCreator subclasses then override.
The structure is as follows:
- Product (e.g.,
Creature): Defines the interface for objects the factory method creates. - ConcreteProduct (e.g.,
Dog,Cat): Implements theProductinterface. - Creator (e.g.,
Spawner): Declares the factory method, which returns an object of typeProduct. It can also define a default implementation. - ConcreteCreator (e.g.,
DogSpawner,CatSpawner): Overrides the factory method to return an instance of a specificConcreteProduct.
The Factory Method - Programming Design Patterns - Ep 1 - C++ Coding
This video provides a practical implementation of the full Factory Method pattern. It's a bit less structured than the previous one, but it correctly demonstrates the polymorphic creator hierarchy, which is the core of this lesson.
Watch the video from 11:23 to 17:32. First, he defines the abstract Spawner class (the Creator) with a pure virtual spawnCreature method. Then, he implements concrete DogSpawner, CatSpawner, etc., (the ConcreteCreators) that override this method to create specific creatures.
The key idea is that a DogSpawner's job is to spawn dogs, and a CatSpawner's job is to spawn cats. The client code can operate on a pointer to a generic Spawner without knowing or caring what specific kind of spawner it is; it just calls the factory method, and polymorphism ensures the correct type of creature is created.
// The Creator base class
class Spawner {
public:
virtual ~Spawner() = default;
// The Factory Method
virtual Creature* spawn() = 0;
// Other business logic can use the spawn() method...
};
// A Concrete Creator
class DogSpawner : public Spawner {
public:
Creature* spawn() override {
return new Dog(); // Subclass decides what to create
}
};
// Another Concrete Creator
class CatSpawner : public Spawner {
public:
Creature* spawn() override {
return new Cat();
}
};
The client might use it like this:
// This spawner could have been chosen at runtime
Spawner* mySpawner = new DogSpawner();
Creature* myCreature = mySpawner->spawn(); // Guaranteed to be a Dog
// ... use myCreature ...
delete myCreature;
delete mySpawner;
4. Under the Hood: Polymorphism, Vtables, and Construction
Your goal is to understand how things work internally. The Factory Method relies entirely on C++'s runtime polymorphism, which is implemented via virtual tables (vtable) and virtual pointers (vptr).
When you call mySpawner->spawn(), the following happens:
- The program follows the
mySpawnerpointer to theDogSpawnerobject in memory. - It finds the object's hidden
vptr, which was set during construction to point to theDogSpawner'svtable. - It looks up the address for the
spawnfunction within thatvtable. - It calls the function at that address, which is
DogSpawner::spawn.

This naturally leads to a question you might have: if we have virtual functions, why can't we have virtual constructors?
This article provides an excellent explanation. It details the object construction process and explains why a 'virtual constructor' is a logical contradiction, then introduces an idiom that serves a similar purpose.
Read the initial section that recaps the object construction process, paying close attention to when the vptr is set for the base and derived classes. Then, read the section that directly answers 'Why don’t we have virtual constructors?'. The analogy of building a house is particularly insightful.
As the article explains, the vtable itself is set up by the constructor. A base class constructor sets the vptr to the base vtable, and then the derived class constructor updates it to the derived vtable. You can't use a mechanism (the vtable) to decide which function to call before that mechanism has even been built.
The Factory Method pattern is, in essence, a primary solution to the problem that constructors can't be virtual. It provides a polymorphic way to create objects.
5. From C-style to Modern C++: Ownership and Slicing
The examples we've seen use raw pointers (new/delete). In modern C++, this is a major red flag. It introduces two severe problems: ambiguous ownership and object slicing.
The Factory Method (Slicing and Ownership Semantics)
This article from Modernes C++ is crucial. It dissects the problems of using raw pointers with polymorphic classes and presents the modern C++ solution.
Please read these three sections carefully: 'Slicing' defines the problem and shows how passing a polymorphic object by value can destroy it. 'Ownership Semantics' explains why raw pointers are ambiguous. Finally, 'The Strong Rescue' demonstrates how std::unique_ptr solves both problems elegantly.
Key takeaways from the article:
- Ownership: A function returning a raw pointer
Creature*is ambiguous. Is the caller now responsible for callingdelete(owning pointer), or is the creator still managing its lifetime (borrowing pointer)? This ambiguity leads to memory leaks or double deletions. - Slicing: If you ever accidentally copy a derived object into a base class object (e.g., by passing it by value), the derived part is "sliced off." You're left with just a base object, and all polymorphic behavior is lost. This is a subtle but deadly bug.
- The Solution: Use smart pointers to manage ownership explicitly.
std::unique_ptris perfect for factories, as it clearly transfers unique ownership of the newly created object to the caller. It also prevents slicing becausestd::unique_ptrcannot be copied.
Let's refactor our Spawner example to use std::unique_ptr:
#include <memory>
class Spawner {
public:
virtual ~Spawner() = default;
// The factory method now returns a smart pointer
virtual std::unique_ptr<Creature> spawn() = 0;
};
class DogSpawner : public Spawner {
public:
std::unique_ptr<Creature> spawn() override {
// std::make_unique is preferred for creating smart pointers
return std::make_unique<Dog>();
}
};
// Client code is now safer and cleaner
std::unique_ptr<Spawner> mySpawner = std::make_unique<DogSpawner>();
std::unique_ptr<Creature> myCreature = mySpawner->spawn(); // Ownership is transferred
// No need to call delete! The object is automatically destroyed when myCreature goes out of scope.
This is the canonical, modern C++ way to implement the Factory Method pattern.
Test your understanding!
You are tasked with creating a system that can connect to different databases (PostgreSQL, MySQL, SQLite). The client code should not know which specific database library it is using.
How would you structure this using the Factory Method pattern and modern C++? Describe the four main components (Product, ConcreteProduct, Creator, ConcreteCreator) for this scenario. What would the signature of your factory method be?
Show answer
-
Product: An abstract base class, say
IDatabaseConnection, with pure virtual functions likeconnect(),query(), anddisconnect().class IDatabaseConnection { public: virtual ~IDatabaseConnection() = default; virtual void connect() = 0; virtual void query(const std::string& q) = 0; }; -
ConcreteProducts: Derived classes like
PostgresConnection,MySqlConnection, etc., that inherit fromIDatabaseConnectionand implement the virtual functions using their specific database APIs. -
Creator: An abstract base class
DatabaseFactorywith a pure virtual factory method.class DatabaseFactory { public: virtual ~DatabaseFactory() = default; virtual std::unique_ptr<IDatabaseConnection> createConnection() = 0; };The factory method signature would be
virtual std::unique_ptr<IDatabaseConnection> createConnection() = 0;. It returns astd::unique_ptrto the base interface to transfer ownership and prevent slicing. -
ConcreteCreators: Subclasses like
PostgresFactory,MySqlFactory, etc. ThePostgresFactorywould implementcreateConnectionto returnstd::make_unique<PostgresConnection>().
Conclusion
Today we've added the Factory Method pattern to your design toolkit. It provides a powerful mechanism for decoupling object creation from client code, making your systems more flexible and easier to maintain.
Key Takeaways:
- The Factory Method pattern defines an interface for creating objects but lets subclasses override a virtual method to decide which concrete class to instantiate.
- It solves the problem of tight coupling, where client code depends directly on concrete classes, by adhering to the Open/Closed Principle.
- The pattern relies on C++'s runtime polymorphism, which is implemented through the vptr/vtable mechanism. This mechanism is set up by constructors, which is why constructors themselves cannot be virtual.
- In modern C++, the factory method should return a smart pointer, typically
std::unique_ptr, to clearly express ownership transfer and prevent dangerous bugs like memory leaks and object slicing.
In the next lesson, we will explore the Builder pattern. While the Factory Method is about creating one of several possible objects from a class hierarchy, the Builder pattern focuses on constructing a single, complex object step-by-step, separating the object's construction from its representation.