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 NotImplementedErrorComplete 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 formatStep-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 : createsCommon 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 registeredInterview 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).