Abstract Factory Pattern
Create families of related objects without specifying concrete classes — virtual dispatch, std::function, and template policy implementations in C++.
Abstract Factorysince C++98A creational pattern that exposes an interface for producing families of mutually compatible objects, letting the entire product suite be swapped atomically by replacing the factory, without modifying client code.
Overview
The Abstract Factory solves a different problem than Factory Method. Factory Method creates one product type and lets subclasses vary the concrete class. Abstract Factory creates a coordinated family: every product returned by one factory instance is guaranteed to work together. Replacing DarkThemeFactory with LightThemeFactory swaps every widget atomically — no risk of a DarkButton appearing beside a LightTextBox.
Three implementation strategies cover the practical landscape:
| Strategy | Mechanism | Family selection | Overhead |
|---|---|---|---|
| Virtual dispatch | Pure virtual base class | Runtime (pointer swap) | vtable per call |
std::function struct | Callable fields | Runtime (field assignment) | Type-erasure cost |
| Template policy | Full specialization | Compile time | Zero |
Syntax
Virtual Abstract Factory (C++98; unique_ptr return requires C++11)
struct WidgetFactory {
virtual std::unique_ptr<Button> make_button(std::string label) = 0; // C++11: unique_ptr
virtual std::unique_ptr<TextBox> make_textbox(std::string placeholder) = 0;
virtual ~WidgetFactory() = default; // C++11: = default
};Each concrete factory is one class that overrides both methods. Client code holds a WidgetFactory& or std::unique_ptr<WidgetFactory> — never the concrete type.
std::function-Based (C++11)
struct WidgetFactories {
std::function<std::unique_ptr<Button>(std::string)> make_button; // C++11
std::function<std::unique_ptr<TextBox>(std::string)> make_textbox;
};No inheritance. Individual fields can be replaced independently — especially useful when stubbing in tests.
Examples
Cross-Platform UI (Virtual Dispatch)
#include <memory> // C++11
#include <string>
#include <iostream>
struct Button { virtual void render() const = 0; virtual ~Button() = default; };
struct TextBox { virtual void render() const = 0; virtual ~TextBox() = default; };
struct WidgetFactory {
virtual std::unique_ptr<Button> make_button(std::string label) = 0;
virtual std::unique_ptr<TextBox> make_textbox(std::string placeholder) = 0;
virtual ~WidgetFactory() = default;
};
// ── Dark family ───────────────────────────────────────────────────────────────
struct DarkButton : Button {
std::string label_;
explicit DarkButton(std::string l) : label_{std::move(l)} {} // C++11: move
void render() const override { std::cout << "[DARK] btn: " << label_ << '\n'; }
};
struct DarkTextBox : TextBox {
std::string ph_;
explicit DarkTextBox(std::string p) : ph_{std::move(p)} {}
void render() const override { std::cout << "[DARK] txt: " << ph_ << '\n'; }
};
struct DarkFactory : WidgetFactory {
std::unique_ptr<Button> make_button(std::string l) override
{ return std::make_unique<DarkButton>(std::move(l)); } // C++14: make_unique
std::unique_ptr<TextBox> make_textbox(std::string p) override
{ return std::make_unique<DarkTextBox>(std::move(p)); }
};
// ── Light family (symmetric — omitted) ────────────────────────────────────────
// ── Client — depends only on the abstract interfaces ─────────────────────────
class LoginForm {
std::unique_ptr<Button> btn_;
std::unique_ptr<TextBox> user_, pass_;
public:
explicit LoginForm(WidgetFactory& f)
: btn_{f.make_button("Sign In")}
, user_{f.make_textbox("Username")}
, pass_{f.make_textbox("Password")} {}
void render() const { user_->render(); pass_->render(); btn_->render(); }
};
DarkFactory dark;
LoginForm form{dark};
form.render();
// Swap to LightFactory — LoginForm is unchangedstd::make_unique arrived in C++14. For C++11 targets use std::unique_ptr<T>(new T(...)).
Runtime-Configurable Factory (C++11)
When factories need to be assembled from plugins or runtime configuration, the callable struct composes cleanly:
struct WidgetFactories {
std::function<std::unique_ptr<Button>(std::string)> make_button;
std::function<std::unique_ptr<TextBox>(std::string)> make_textbox;
};
WidgetFactories dark_set{
[](std::string l) { return std::make_unique<DarkButton>(std::move(l)); },
[](std::string p) { return std::make_unique<DarkTextBox>(std::move(p)); }
};
// Test stub: replace one field with a no-op lambda, no subclass needed
WidgetFactories test_set = dark_set; // C++11: copyable
test_set.make_button = [](std::string) {
return std::make_unique<NullButton>(); // records calls, renders nothing
};This deliberately breaks the consistency guarantee of the classic pattern — only appropriate when mixing families is domain-correct (e.g., A/B testing individual widget variants).
Template Policy Factory (C++11 specializations; zero overhead)
When the family is known at compile time, eliminate the vtable entirely:
struct DarkTheme {};
struct LightTheme {};
template<typename Theme>
struct Widgets; // primary template left undefined; specializations are the contract
template<>
struct Widgets<DarkTheme> {
static std::unique_ptr<Button> make_button(std::string l)
{ return std::make_unique<DarkButton>(std::move(l)); }
static std::unique_ptr<TextBox> make_textbox(std::string p)
{ return std::make_unique<DarkTextBox>(std::move(p)); }
};
template<>
struct Widgets<LightTheme> {
static std::unique_ptr<Button> make_button(std::string l)
{ return std::make_unique<LightButton>(std::move(l)); }
static std::unique_ptr<TextBox> make_textbox(std::string p)
{ return std::make_unique<LightTextBox>(std::move(p)); }
};
template<typename Theme>
class LoginForm {
std::unique_ptr<Button> btn_;
std::unique_ptr<TextBox> user_, pass_;
public:
LoginForm()
: btn_{Widgets<Theme>::make_button("Sign In")}
, user_{Widgets<Theme>::make_textbox("Username")}
, pass_{Widgets<Theme>::make_textbox("Password")} {}
void render() const { user_->render(); pass_->render(); btn_->render(); }
};
LoginForm<DarkTheme> dark_form; // no vtable, no std::function allocation
LoginForm<LightTheme> light_form;The compiler instantiates only the specializations actually used; factory calls are inlined. The trade-off is hard: the family cannot be selected from a config file at startup.
Dependency Injection: Database Drivers
The pattern's most common production use is swappable infrastructure:
struct Connection {
virtual bool execute(std::string_view sql) = 0; // C++17: string_view
virtual ~Connection() = default;
};
struct PreparedStmt {
virtual bool bind(int pos, std::string_view val) = 0;
virtual bool step() = 0;
virtual ~PreparedStmt() = default;
};
struct DbFactory {
virtual std::unique_ptr<Connection> connect(std::string_view dsn) = 0;
virtual std::unique_ptr<PreparedStmt> prepare(Connection&, std::string_view sql) = 0;
virtual ~DbFactory() = default;
};
struct PostgresFactory : DbFactory { /* libpq wrappers */ };
struct SQLiteFactory : DbFactory { /* sqlite3 wrappers */ };
struct MockDbFactory : DbFactory { /* deterministic stubs for tests */ };
void run_migration(DbFactory& db) {
auto conn = db.connect("postgresql://localhost/prod");
auto stmt = db.prepare(*conn, "CREATE TABLE IF NOT EXISTS events (id BIGSERIAL PRIMARY KEY)");
stmt->step();
}
// In tests: run_migration(mock);
// In production: run_migration(postgres);std::string_view is C++17. For C++11/14 targets use const std::string&.
Best Practices
Return std::unique_ptr, never raw pointers. Factory methods are the textbook case for unique_ptr (C++11): ownership transfers to the caller; the factory never retains the product. Raw pointer returns require the caller to know which delete form to use — that knowledge breaks encapsulation.
Keep the factory interface narrow. Every product type in the abstract factory is a permanent API commitment every concrete factory must fulfill. Adding make_tooltip() touches the base class and every existing subclass. If some callers only need a subset of products, split into smaller focused factories.
Prefer the std::function struct for unit-tested code. Stubbing a virtual factory requires deriving a new class. A callable struct needs only a lambda per field — significantly less boilerplate in test fixtures. The virtual variant is better when the concrete factories live in separate translation units or plugins.
Inject factories via constructor parameters, not singletons. A global WidgetFactory::instance() couples every component to a specific family and makes parallel test runs with different factories impossible. Pass WidgetFactory& or const WidgetFactories& directly into constructors.
Use the template variant only when the family is compile-time-fixed. If the choice depends on a command-line flag, a config file, or a dynamically loaded plugin, you need runtime dispatch — virtual or std::function. Misapplying the template variant produces code that can't be reconfigured without recompilation.
Common Pitfalls
Mixing products from different families. DarkButton + LightTextBox silently compiles and produces inconsistent output. The factory is the single authority enforcing compatibility; storing factories as mutable values or having multiple active factories in one scope makes this guarantee fragile.
Covariant returns break with unique_ptr. Unlike raw pointers, std::unique_ptr<Derived> is not covariant with std::unique_ptr<Base> in C++. You cannot declare an override as virtual std::unique_ptr<DarkButton> make_button(...) over a base that returns unique_ptr<Button> — it will not compile as an override. All overrides must return unique_ptr<Base>.
Factory interface bloat. As product types accumulate, the abstract factory becomes a god interface. Every concrete factory must implement every method, even for product types it doesn't logically own. Audit frequently whether you have a genuine coordinated family or an unrelated collection of creation methods that should live in separate factories.
Missing virtual destructors. Every abstract product interface (Button, TextBox) and the factory itself requires virtual ~T() = default. Destroying a DarkButton through a Button* without a virtual destructor is undefined behavior, not a runtime warning.
Template-Based Abstract Factory
Template factories achieve zero-cost abstraction by resolving product types at compile time. A Factory<T> primary template declares a create() interface; partial or full specializations supply the concrete products. No vtable is allocated — every call is a direct, inlinable function invocation.
// C++11
#include <memory>
#include <string>
struct Button { virtual void render() const = 0; virtual ~Button() = default; };
struct DarkButton : Button {
void render() const override { /* dark rendering */ }
};
// Primary template — intentionally undefined; only specializations are valid
template<typename T>
struct Factory;
// Specialization for Button
template<>
struct Factory<Button> {
virtual std::unique_ptr<Button> create() = 0;
virtual ~Factory() = default;
};
// Concrete specialization — statically dispatched
template<typename ConcreteProduct>
struct ConcreteFactory : Factory<Button> {
std::unique_ptr<Button> create() override {
return std::unique_ptr<Button>(new ConcreteProduct{});
}
};
ConcreteFactory<DarkButton> dark_factory;
auto btn = dark_factory.create(); // returns unique_ptr<Button> owning a DarkButtonThe template version is zero-cost when the concrete factory type is known at the call site; the virtual version is runtime-configurable. Choose the template variant when family selection is a compile-time decision; choose the virtual variant when it must be deferred to startup or configuration.
Registering Factories at Runtime
A registry maps string keys to factory callables, enabling product creation by name without compile-time knowledge of the concrete type. This is the canonical enabler for plugin architectures: a plugin registers its factory under a known name, and the host creates products without including the plugin's headers.
// C++11
#include <functional>
#include <memory>
#include <string>
#include <unordered_map>
#include <stdexcept>
struct Widget { virtual void draw() const = 0; virtual ~Widget() = default; };
struct DarkButton : Widget { void draw() const override { /* ... */ } };
class FactoryRegistry {
std::unordered_map<std::string,
std::function<std::unique_ptr<Widget>()>> factories_;
public:
void register_factory(std::string name,
std::function<std::unique_ptr<Widget>()> fn) {
factories_.emplace(std::move(name), std::move(fn));
}
std::unique_ptr<Widget> create(const std::string& name) const {
auto it = factories_.find(name);
if (it == factories_.end())
throw std::runtime_error{"Unknown widget: " + name};
return it->second();
}
};
// Registration — typically called during plugin initialisation
FactoryRegistry registry;
registry.register_factory("Button", []() {
return std::unique_ptr<Widget>(new DarkButton{});
});
// Creation — the caller knows only the string key
auto widget = registry.create("Button");
widget->draw();The std::function value type means any callable — lambda, functor, or free function — can serve as a factory. Prefer this pattern over hard-coded if/switch chains whenever new widget types must be added without recompiling the registry.
Abstract Factory with Dependency Injection
Passing an AbstractFactory& as a constructor parameter rather than fetching it from a global makes components independently testable. The constructor documents the dependency explicitly; tests supply a mock factory without any link-time or preprocessor tricks.
// C++11
#include <memory>
#include <string>
struct Button { virtual void render() const = 0; virtual ~Button() = default; };
struct TextBox { virtual void render() const = 0; virtual ~TextBox() = default; };
struct WidgetFactory {
virtual std::unique_ptr<Button> make_button(std::string label) = 0;
virtual std::unique_ptr<TextBox> make_textbox(std::string placeholder) = 0;
virtual ~WidgetFactory() = default;
};
// Production renderer — receives the factory by reference
class Renderer {
std::unique_ptr<Button> btn_;
std::unique_ptr<TextBox> field_;
public:
explicit Renderer(WidgetFactory& factory) // DI: no global
: btn_{factory.make_button("OK")}
, field_{factory.make_textbox("Enter value")} {}
void render() const { field_->render(); btn_->render(); }
};
// ── Test double ───────────────────────────────────────────────────────────────
struct NullButton : Button { void render() const override {} };
struct NullTextBox : TextBox { void render() const override {} };
struct MockWidgetFactory : WidgetFactory {
int button_count = 0;
int textbox_count = 0;
std::unique_ptr<Button> make_button(std::string) override {
++button_count;
return std::unique_ptr<Button>(new NullButton{});
}
std::unique_ptr<TextBox> make_textbox(std::string) override {
++textbox_count;
return std::unique_ptr<TextBox>(new NullTextBox{});
}
};
// In a test:
MockWidgetFactory mock;
Renderer r{mock};
r.render();
// assert mock.button_count == 1 && mock.textbox_count == 1;A singleton-based factory (WidgetFactory::instance()) makes this impossible: the test would mutate global state and break parallel test runs. DI turns a global coupling into a local interface dependency — the hallmark of testable design.
std::function-Based Factory
std::function wraps any callable into a uniform, copyable, assignable factory value — no class hierarchy required. Defining using ButtonFactory = std::function<std::unique_ptr<Button>()> creates a first-class factory type that can be stored, passed, and composed like any other value.
// C++11
#include <functional>
#include <memory>
#include <string>
struct Button { virtual void render() const = 0; virtual ~Button() = default; };
struct DarkButton : Button {
std::string style_;
explicit DarkButton(std::string s) : style_{std::move(s)} {}
void render() const override { /* use style_ */ }
};
using ButtonFactory = std::function<std::unique_ptr<Button>()>;
// Factory that captures state — impossible with a plain function pointer
std::string active_style = "dark-high-contrast";
ButtonFactory dark_button_factory = [active_style]() {
return std::unique_ptr<Button>(new DarkButton{active_style});
};
// Accept a factory as a parameter — the caller decides what gets created
void populate_toolbar(ButtonFactory make_btn) {
auto b1 = make_btn();
auto b2 = make_btn();
b1->render();
b2->render();
}
populate_toolbar(dark_button_factory);
// Compose factories: wrap an existing factory to add logging
ButtonFactory logged_factory = [inner = dark_button_factory]() {
auto btn = inner();
/* log creation */
return btn;
};Lambdas captured in std::function avoid vtable overhead on the product hierarchy itself while keeping runtime flexibility. When a subsystem needs only one creation function rather than a full family, a std::function factory is lighter-weight than defining an abstract base class.
Abstract Factory vs Factory Method vs Builder
Each creational pattern answers a different question about object construction. Abstract Factory governs which family of objects to create; Factory Method governs which single product variant to create; Builder governs how a complex object's construction steps are sequenced.
| Pattern | What varies | Call-site knowledge | Unit of work | Flexibility |
|---|---|---|---|---|
| Abstract Factory | Whole family of products | Knows only the abstract factory type | Multiple coordinated products at once | Swap entire product suite by replacing the factory |
| Factory Method | One product per class hierarchy | Knows only the base class | One product per call | Override a single creation step in a subclass |
| Builder | Construction steps of one complex object | Knows the builder interface | One fully configured object | Reorder, skip, or repeat steps; same builder, different configs |
Abstract Factory is overkill for single-product families — use Factory Method there. If object creation involves many optional parts, conditional steps, or a mandatory finalisation call, Builder is the right tool; Abstract Factory does not model multi-step construction.
Abstract Factory in Plugin Systems
Dynamic libraries extend an application at runtime by exporting a factory creator function with C linkage, ensuring a stable ABI across compiler versions. The host loads the library, resolves the symbol, and invokes it to obtain a heap-allocated factory; a paired destroy_factory function is used as the shared_ptr deleter to guarantee symmetric allocation.
// C++11 (POSIX dlopen; replace with LoadLibrary/GetProcAddress on Windows)
#include <dlfcn.h>
#include <memory>
#include <stdexcept>
#include <string>
struct AbstractFactory {
virtual int version() const = 0; // ABI versioning
virtual std::unique_ptr<Widget> make_widget() = 0;
virtual ~AbstractFactory() = default;
};
// ── Symbols the plugin must export (C linkage for ABI stability) ──────────────
// extern "C" AbstractFactory* create_factory();
// extern "C" void destroy_factory(AbstractFactory*);
constexpr int REQUIRED_FACTORY_VERSION = 2;
std::shared_ptr<AbstractFactory> load_plugin(const std::string& path) {
void* handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL);
if (!handle)
throw std::runtime_error{dlerror()};
using CreateFn = AbstractFactory*(*)();
using DestroyFn = void(*)(AbstractFactory*);
auto create = reinterpret_cast<CreateFn> (dlsym(handle, "create_factory"));
auto destroy = reinterpret_cast<DestroyFn>(dlsym(handle, "destroy_factory"));
if (!create || !destroy)
throw std::runtime_error{"Plugin missing required symbols"};
AbstractFactory* raw = create();
if (!raw || raw->version() != REQUIRED_FACTORY_VERSION)
throw std::runtime_error{"Incompatible plugin version"};
// Custom deleter: call destroy_factory, then close the library handle
return std::shared_ptr<AbstractFactory>(raw,
[handle, destroy](AbstractFactory* f) {
destroy(f);
dlclose(handle);
});
}
// Usage
auto factory = load_plugin("./libdark_theme.so");
auto widget = factory->make_widget();
widget->draw();The extern "C" declaration strips C++ name-mangling, so the symbol name is predictable regardless of which compiler built the plugin. Always verify the factory's version before use; a mismatch between host and plugin ABIs causes silent data corruption or crashes, not a clean error.
See Also
- Factory Method — creates one product type; Abstract Factory composes multiple coordinated Factory Methods into a family contract
- CRTP — the static polymorphism technique that underpins zero-overhead template policy factories
- Type Erasure — the mechanism behind
std::function; relevant when reasoning about callable-factory allocation and overhead - RAII —
unique_ptr-based factory returns depend on RAII for correct ownership transfer and exception safety