Factory Method

Learning Objectives

Implement Factory Method with an overridable creation method.

Compare it with a simple factory function.

Decide when subclass-based creation is justified.

Prerequisites: Modules 7, 8.

Intuition and Definition

Intent: define an interface for creating an object, but let subclasses decide which class to instantiate.

Problem: a general algorithm (export a report, run a diagnostic session) needs to create a product object, but the concrete product depends on the variant (PDF/CSV, UDS/OBD).

Participants: Creator (declares create(), often with a template algorithm that uses it), ConcreteCreator (overrides create()), Product interface, ConcreteProduct.

A simple factory — a function or static method with a switch that returns different products — is not the GoF pattern, but is often all you need.

Why It Matters

Creation logic tends to leak new ConcreteThing into business code. Factory Method keeps the algorithm generic and puts the choice of concrete class in one overridable place.

Syntax and Language Notes

class Exporter:
    def export(self, rows):
        writer = self.create_writer()     # factory method
        return writer.write(rows)
    def create_writer(self):
        raise NotImplementedError

Complete Example

#include <format>
#include <iostream>
#include <memory>
#include <string>
#include <vector>

struct Writer {                                          // Product
    virtual ~Writer() = default;
    virtual std::string header() const = 0;
    virtual std::string row(const std::string& a, double b) const = 0;
};
struct CsvWriter : Writer {
    std::string header() const override { return "sensor,value"; }
    std::string row(const std::string& a, double b) const override { return std::format("{},{:.2f}", a, b); }
};
struct MarkdownWriter : Writer {
    std::string header() const override { return "| sensor | value |\n|---|---|"; }
    std::string row(const std::string& a, double b) const override { return std::format("| {} | {:.2f} |", a, b); }
};

class ReportExporter {                                   // Creator
public:
    virtual ~ReportExporter() = default;
    void exportReport(const std::vector<std::pair<std::string, double>>& data) const {   // algorithm uses the product
        auto w = createWriter();                         // factory method
        std::cout << w->header() << "\n";
        for (const auto& [k, v] : data) std::cout << w->row(k, v) << "\n";
    }
protected
…
sensor,value
coolant,92.50
oil,104.25
| sensor | value |
|---|---|
| coolant | 92.50 |
| oil | 104.25 |
simple factory: markdown writer, unknown format

Step-by-Step Walkthrough

exportReport is written once; it doesn't know which writer it uses.

CsvExporter and MarkdownExporter override only createWriter().

The simple factory makeWriter chooses by a run-time key — usually enough when there's no algorithm to share.

classDiagram
  class ReportExporter {
    <<abstract>>
    +exportReport(data)
    #createWriter()* Writer
  }
  class Writer {
    <<interface>>
  }
  ReportExporter <|-- CsvExporter
  ReportExporter <|-- MarkdownExporter
  Writer <|.. CsvWriter
  Writer <|.. MarkdownWriter
  ReportExporter ..> Writer : creates

Common Mistakes

A creator subclass per product when a lambda or a map from key to constructor would do.

Factories that return raw owning pointers — return unique_ptr.

Calling the factory method from the base constructor (virtual dispatch doesn't reach the subclass yet — Module 5).

Trade-offs and Alternatives

Factory Method uses inheritance to vary creation; alternatives: pass a factory function (std::function<std::unique_ptr<Writer>()>), a registry map from names to creators (plugin systems), or Abstract Factory when *families* of products vary together.

Real-World Use

Frameworks: Application::createDocument() in document editors, Collection.iterator() in Java (each collection creates its own iterator), and test frameworks that create fixtures through overridable methods.

Practice

Q1. (Easy) Which participant decides the concrete product?

Answer: The concrete creator, by overriding the factory method.

Q2. (Medium) When is a simple factory function preferable?

Answer: When creation is chosen by data (a config key) and there's no shared algorithm in a creator class.

Q3. (Advanced) Replace the creator subclasses with a registry that maps format names to creator functions, so plug-ins can register new formats.

#include <functional>
#include <iostream>
#include <map>
#include <memory>
#include <string>
struct Writer { virtual ~Writer() = default; virtual std::string name() const = 0; };
struct Csv : Writer { std::string name() const override { return "csv"; } };
struct Json : Writer { std::string name() const override { return "json"; } };
class WriterRegistry {
    std::map<std::string, std::function<std::unique_ptr<Writer>()>> makers_;
public:
    void add(const std::string& key, std::function<std::unique_ptr<Writer>()> f) { makers_[key] = std::move(f); }
    std::unique_ptr<Writer> make(const std::string& key) const { auto it = makers_.find(key); return it == makers_.end() ? nullptr : it->second(); }
};
int main() {
    WriterRegistry r;
    r.add("csv", [] { return std::make_unique<Csv>(); });
    r.add("json", [] { return std::make_unique<Json>(); });     // a plug-in registering itself
    for (const char* k : {"json", "csv", "pdf"}) { auto w = r.make(k); std::cout << k << " -> " << (w ? w->name() : "not registered") << "\n"; }
}
json -> json
csv -> csv
pdf -> not registered

Interview Question

Q1. "Factory Method vs simple factory — what's the difference?"

Answer: A simple factory is one function (often static) that chooses a concrete class from parameters — creation is centralized but not extensible without editing it. Factory Method is an overridable method on a creator class: subclasses choose the product, and the creator usually contains an algorithm that uses it, so new variants are added by subclassing without modifying existing code (OCP). Use the simple factory unless you have that shared algorithm and expect new variants.

Quick Quiz

Q1. What does the factory method return?

Answer: A product through its abstract interface.

Q2. Factory Method relies on which OOP mechanism to vary creation?

Answer: Inheritance and overriding.

Q3. Name a lighter-weight alternative.

Answer: Passing a creator function or using a registry map.

Summary

Factory Method lets subclasses choose the concrete product used by a base algorithm.

Simple factory functions and registries are often enough.

Related: Module 29 (Abstract Factory), Module 44 (Template Method).