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

  • 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 itselfCreator::operation() calls Creator::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. Adding MarkdownApp doesn'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 Markdown requires 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 switch or if/else is 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 returns T is just a function.
  • Returning raw new T → caller-owned leak risk. Always return std::unique_ptr<Product> (or shared_ptr if shared ownership).
  • Slicing when factory returns Product by value → loses the derived part. Always return a pointer or reference to the polymorphic base.
  • virtual in constructor — base ctor cannot call factoryMethod() 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