Factory Method Pattern
- Description: Defer instantiation to subclasses or callbacks — base defines the algorithm, derived (or a function) decides which concrete product to create; covers classic virtual form, modern
std::function/template alternatives, and the distinction from a "static factory function". - My Notion Note ID: K2C-2-2
- Created: 2026-05-22
- Updated: 2026-05-22
- License: Reuse is very welcome. Please credit Yu Zhang and link back to the original on yuzhang.io
Table of Contents
- 1. Intent
- 2. Structure
- 3. C++ Implementations
- 4. When to Use, When Not To
- 5. Pitfalls
- 6. Related Patterns
- 7. References
1. Intent
- Defer construction to a subclass. Base class fixes the workflow; the which concrete class to instantiate decision moves into an overridable hook.
- Decouples client code from concrete product names → client depends only on the product interface.
- Useful when the creation step is one detail in a longer template-method-style algorithm that lives in the base.
2. Structure
| Role | Responsibility |
|---|---|
Product |
Interface returned by the factory. |
ConcreteProduct |
Implementation of Product. |
Creator |
Declares factoryMethod() (often virtual, sometimes pure); calls it inside higher-level operations. |
ConcreteCreator |
Overrides factoryMethod() to return a specific ConcreteProduct. |
- The pattern lives in
Creator's relationship with itself —Creator::operation()callsCreator::factoryMethod()polymorphically. Not just "a function that returns an object".
3. C++ Implementations
3.1 Classic GoF form
#include <memory>
#include <iostream>
struct Document {
virtual ~Document() = default;
virtual void open() = 0;
virtual void close() = 0;
};
struct TextDocument : Document {
void open() override { std::cout << "open text\n"; }
void close() override { std::cout << "close text\n"; }
};
struct PdfDocument : Document {
void open() override { std::cout << "open pdf\n"; }
void close() override { std::cout << "close pdf\n"; }
};
struct Application {
virtual ~Application() = default;
void newDocument() {
auto doc = createDocument(); // factory method
doc->open();
docs_.push_back(std::move(doc));
}
protected:
virtual std::unique_ptr<Document> createDocument() = 0;
private:
std::vector<std::unique_ptr<Document>> docs_;
};
struct TextApp : Application {
protected:
std::unique_ptr<Document> createDocument() override {
return std::make_unique<TextDocument>();
}
};
struct PdfApp : Application {
protected:
std::unique_ptr<Document> createDocument() override {
return std::make_unique<PdfDocument>();
}
};
int main() {
TextApp app;
app.newDocument(); // workflow defined in base, product chosen by subclass
}
Application::newDocument()knows the steps. Subclass owns the choice. AddingMarkdownAppdoesn't touch base.
3.2 Parameterized factory (single class, switch on tag)
enum class DocKind { Text, Pdf };
std::unique_ptr<Document> makeDocument(DocKind k) {
switch (k) {
case DocKind::Text: return std::make_unique<TextDocument>();
case DocKind::Pdf: return std::make_unique<PdfDocument>();
}
std::unreachable(); // C++23
}
- Adding
Markdownrequires editing the function → not Open/Closed. Acceptable when the set of products is closed and known.
3.3 Modern alternative — std::function factory map
#include <functional>
#include <unordered_map>
#include <string>
class DocumentRegistry {
public:
using Factory = std::function<std::unique_ptr<Document>()>;
void registerType(std::string name, Factory f) {
factories_[std::move(name)] = std::move(f);
}
std::unique_ptr<Document> create(const std::string& name) const {
auto it = factories_.find(name);
return it != factories_.end() ? it->second() : nullptr;
}
private:
std::unordered_map<std::string, Factory> factories_;
};
// Wiring
DocumentRegistry reg;
reg.registerType("text", []{ return std::make_unique<TextDocument>(); });
reg.registerType("pdf", []{ return std::make_unique<PdfDocument>(); });
auto doc = reg.create("pdf");
- Plug-in-style — new product types register themselves at startup. Open/Closed without inheritance on the creator.
3.4 Template-based factory
template <class ProductT>
struct GenericApp {
void newDocument() {
ProductT doc;
doc.open();
}
};
GenericApp<PdfDocument> app;
app.newDocument();
- Trades runtime flexibility for type safety + inlining. Useful when the product is known at compile time.
4. When to Use, When Not To
Use when:
- A base class workflow has a single creation step that subclasses should customize.
- Client code shouldn't know the concrete product class names.
- You want to add new product types without editing existing code (Open/Closed).
Avoid when:
- Only one concrete product exists — direct construction is clearer than ceremony.
- The product set is closed and small — a
switchorif/elseis shorter and more debuggable. - "Factory" only means "the constructor takes too many args" — that's a hint to apply Builder, not Factory Method.
5. Pitfalls
- Confusing Factory Method with a static factory function (e.g.
std::make_unique). The pattern's defining trait is polymorphic override in a derived creator. A plain helper that returnsTis just a function. - Returning raw
new T→ caller-owned leak risk. Always returnstd::unique_ptr<Product>(orshared_ptrif shared ownership). - Slicing when factory returns
Productby value → loses the derived part. Always return a pointer or reference to the polymorphic base. virtualin constructor — base ctor cannot callfactoryMethod()and get derived behaviour. Two-phase init (init()after construction) is one workaround.- Performance — every factory call is a virtual dispatch. Not free in tight loops; consider template form (§ 3.4).
6. Related Patterns
- Factory Method vs Abstract Factory — Factory Method creates one product via inheritance + override. Abstract Factory creates a family of related products via composition (an object whose methods are factory methods). See Abstract Factory Pattern.
- Factory Method vs Builder — Factory Method returns the object in one step; Builder constructs in many steps with intermediate state. Builder used when ctor would take 8+ parameters.
- Factory Method vs Prototype — Prototype creates by cloning an existing instance; Factory Method by calling a constructor. Prototype wins when configuring an object is more expensive than copying one.
- Factory Method vs Template Method — Template Method generalizes any algorithm step; Factory Method specializes Template Method for the creation step specifically.
- Factory Method vs Dependency Injection — DI passes the product in from outside; Factory Method creates it inside the class. DI usually preferable when testability matters.
7. References
- Factory Method — refactoring.guru
- Factory Method — sourcemaking
- GoF, Design Patterns, ch. 3 — Factory Method (p. 107)
- cppreference —
std::function - Herb Sutter, "Virtuality"