GoF Design Patterns - Abstract Factory
A practical guide to the Abstract Factory pattern from the GoF collection: concept, when to use it, a C++ implementation example, caveats, and references.
Hello! I'm Pan-kun.
This entry covers the
Abstract Factory pattern
from the GoF (Gang of
Four) design
patterns.
I'll
give a practical
explanation including
sample code (C++), how
to build it, when to use
it, caveats, and
references.
Introduction
The Abstract Factory is a pattern that provides an interface for creating families of related or dependent objects without specifying their concrete classes.
As a primary reference, here's the Wikipedia definition:
"Provide an interface for creating families of related or dependent objects without specifying their concrete classes."
https://en.wikipedia.org/wiki/Abstract_factory_pattern
"Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses."
(Source: https://en.wikipedia.org/wiki/Abstract_factory_pattern)
As the definitions indicate, this pattern allows the client to work only with abstract interfaces and remain unaware of which concrete products are being created. By swapping implementations, you can replace an entire theme (family of products) consistently.
When to use it
Here are some typical scenarios where you might consider using Abstract Factory:
- When you want to switch UI themes or platform-dependent UI components as a group.
-
When you need to
create a set of
products that must
be consistent with
each other (for
example, switching
pairs like
ButtonandCheckbox). - When implementations must be swappable at runtime or through configuration.
Alternatives and considerations:
-
For simple cases, a
Simple Factory(conditional creation) may be sufficient. -
If you need to vary
only a single
product,
Factory Methodmight be more appropriate. - Combining Abstract Factory with dependency injection (DI) improves testability.
Structure (Elements)
-
AbstractFactory: the factory interface for creating product families. -
ConcreteFactory: implements the abstract factory and creates concrete products. -
AbstractProductA,AbstractProductB: abstract product interfaces. -
ConcreteProductA1,ConcreteProductB1: concrete product implementations. -
Client: obtains products from the abstract factory and uses them via abstract interfaces.
A UML diagram is omitted here, but the above components form the basic structure.
C++ Implementation Example
Below is a concise C++
sample implementing an
Abstract Factory for a
Button /
Checkbox
product family.
// AbstractFactory C++
#include <memory>
#include <iostream>
#include <string>
// Abstract Product A: Button
struct Button {
virtual ~Button() = default;
virtual std::string render() const = 0;
};
// Abstract Product B: Checkbox
struct Checkbox {
virtual ~Checkbox() = default;
virtual std::string render() const = 0;
};
// Concrete product family 1: Windows
struct WindowsButton : Button {
std::string render() const override { return "WindowsButton"; }
};
struct WindowsCheckbox : Checkbox {
std::string render() const override { return "WindowsCheckbox"; }
};
// Concrete product family 2: Mac
struct MacButton : Button {
std::string render() const override { return "MacButton"; }
};
struct MacCheckbox : Checkbox {
std::string render() const override { return "MacCheckbox"; }
};
// Abstract Factory
struct GUIFactory {
virtual ~GUIFactory() = default;
virtual std::unique_ptr<Button> createButton() const = 0;
virtual std::unique_ptr<Checkbox> createCheckbox() const = 0;
};
// Concrete Factory: Windows
struct WindowsFactory : GUIFactory {
std::unique_ptr<Button> createButton() const override {
return std::make_unique<WindowsButton>();
}
std::unique_ptr<Checkbox> createCheckbox() const override {
return std::make_unique<WindowsCheckbox>();
}
};
// Concrete Factory: Mac
struct MacFactory : GUIFactory {
std::unique_ptr<Button> createButton() const override {
return std::make_unique<MacButton>();
}
std::unique_ptr<Checkbox> createCheckbox() const override {
return std::make_unique<MacCheckbox>();
}
};
// Client depends on GUIFactory (depends on abstraction)
void renderUI(const GUIFactory& factory) {
auto btn = factory.createButton();
auto chk = factory.createCheckbox();
std::cout << "Render: " << btn->render() << ", " << chk->render() << "\n";
}
int main() {
WindowsFactory wf;
MacFactory mf;
renderUI(wf); // Render: WindowsButton, WindowsCheckbox
renderUI(mf); // Render: MacButton, MacCheckbox
return 0;
}
Key points from the sample:
-
renderUIdoes not know concrete classes; it just uses products provided byGUIFactory. -
Adding a new theme
(product family)
does not require
changes to
renderUI.
Advantages / Disadvantages
Advantages
- You can create families of products consistently, making implementation swaps straightforward.
- Clients are decoupled from concrete classes, improving portability and testability.
- Introducing a new product family typically requires minimal changes to client code.
Disadvantages
-
Adding a new product
type (a new
AbstractProduct
interface) requires
modifying every
ConcreteFactory, which can be costly. - If you pick the wrong abstraction granularity early, you may over-engineer and increase complexity.
- Swapping implementations can affect serialization, APIs, and data formats, which may introduce compatibility issues.
Conclusion
The Abstract Factory is
a powerful pattern when
you need to swap related
product families as a
whole.
Before
adopting it, evaluate
the scope of products to
be added, your test
strategy, and the impact
on
APIs/serialization.
Combining
it with dependency
injection improves
testability and
flexibility. Start small
and extend abstractions
as needed to avoid
premature
over-engineering.
References:
en.wikipedia.orgAbstract
factory
pattern -
Wikipedia
I'll cover another GoF pattern next time. The code samples shown here are simplified for learning purposes — review and adapt them to your production requirements before use.
Thanks for reading — Pan-kun!
Loading comments...