Abstract Factory Pattern
TL;DR — Abstract Factory Pattern
- Category: Creational Design Pattern
- Purpose: Create families of related objects without specifying their concrete classes.
- Key Idea: Ensure products from the same family are used together (consistency guarantee).
- When to Use:
- Cross-platform systems (Windows/Mac/Linux UI)
- Theme engines (Dark/Light mode components)
- Plugin or backend architectures
- Main Advantage: Enforces product compatibility and supports runtime switching of families.
- Main Trade-off: Increases number of classes and design complexity.
- Do NOT Use When: Only one product type exists or no product families are required.
- Difference from Factory Method: Factory Method creates one product; Abstract Factory creates multiple related products.
1. Introduction
The Abstract Factory pattern is a Creational Design Pattern that provides an interface for creating families of related or dependent objects without specifying their concrete classes.
Unlike the Factory Method (which creates a single product), the Abstract Factory creates multiple related products designed to work together.
The Core Problem
Consider a cross-platform GUI framework. A Windows UI must use:
- WindowsButton
- WindowsCheckbox
- WindowsScrollBar
A Mac UI must use:
- MacButton
- MacCheckbox
- MacScrollBar
If clients instantiate objects directly, product families may become mixed:
// WRONG: Mixing product families
Button* btn = new WindowsButton();
Checkbox* chk = new MacCheckbox();
This breaks consistency and may cause runtime incompatibility.
Abstract Factory ensures that products from the same family are used together.
2. Why Do We Need “Abstract Factory”?
Intent:
Provide an interface for creating families of related or dependent objects without specifying their concrete classes. This ensures the objects you construct together are compatible and stylistically consistent (e.g., all "Windows‑style" widgets vs. all "Mac‑style" widgets).
Motivations
- Consistency across a product family: If your UI has Button, Checkbox, Menu, they should all match the same platform/theme/vendor.
- Decoupled creation: Clients depend on abstractions (Button, Checkbox, GUIFactory) rather than concrete classes.
- Open/Closed Principle: Add a new family by implementing a new concrete factory and its products—without modifying client code.
Trade‑offs
- More classes (factories + product variants).
- If you frequently add new product types (e.g., add Slider), you must extend the abstract factory and update all concrete factories.
3. Structure of Abstract Factory
Participants
- AbstractFactory – Declares creation methods
- ConcreteFactory – Implements family-specific creation
- AbstractProductA / B – Product interfaces
- ConcreteProductA1, B1 – Concrete implementations
4. How to Use It (Step‑by‑Step)
- Define product interfaces (e.g., Button, Checkbox).
- Define the abstract factory (e.g., GUIFactory) that declares creation methods for each product type.
- Implement concrete products for each family (e.g., WindowsButton, WindowsCheckbox; MacButton, MacCheckbox).
- Implement concrete factories (e.g., WindowsFactory, MacFactory) that return matching concrete products.
- Inject or choose the concrete factory at runtime (config, environment, DI).
- Use only abstractions in the client; never new a concrete product directly in client code.
5. How It Works (Mechanics)
- The abstract factory groups creation methods for several related product types.
- Each concrete factory creates matching products, guaranteeing compatibility (no Windows button with a Mac checkbox).
- Client code receives/holds a reference to the abstract factory and asks it to create products; the client talks to product interfaces only.
- Swapping families is as simple as changing which factory you instantiate/inject.
6. UML Diagram (Graphical)
7. C++ Implementation Example
Abstract Products
class Button {
public:
virtual void paint() = 0;
virtual ~Button() = default;
};
class Checkbox {
public:
virtual void render() = 0;
virtual ~Checkbox() = default;
};
Concrete Products (Windows)
class WindowsButton : public Button {
public:
void paint() override {
std::cout << "Windows Button\n";
}
};
class WindowsCheckbox : public Checkbox {
public:
void render() override {
std::cout << "Windows Checkbox\n";
}
};
Concrete Products (Mac)
class MacButton : public Button {
public:
void paint() override {
std::cout << "Mac Button\n";
}
};
class MacCheckbox : public Checkbox {
public:
void render() override {
std::cout << "Mac Checkbox\n";
}
};
Abstract Factory
class GUIFactory {
public:
virtual std::unique_ptr<Button> createButton() = 0;
virtual std::unique_ptr<Checkbox> createCheckbox() = 0;
virtual ~GUIFactory() = default;
};
Concrete Factories
class WindowsFactory : public GUIFactory {
public:
std::unique_ptr<Button> createButton() override {
return std::make_unique<WindowsButton>();
}
std::unique_ptr<Checkbox> createCheckbox() override {
return std::make_unique<WindowsCheckbox>();
}
};
class MacFactory : public GUIFactory {
public:
std::unique_ptr<Button> createButton() override {
return std::make_unique<MacButton>();
}
std::unique_ptr<Checkbox> createCheckbox() override {
return std::make_unique<MacCheckbox>();
}
};
Usage
int main() {
std::unique_ptr<GUIFactory> factory;
std::string os = "Windows";
if (os == "Windows")
factory = std::make_unique<WindowsFactory>();
else
factory = std::make_unique<MacFactory>();
auto btn = factory->createButton();
auto chk = factory->createCheckbox();
btn->paint();
chk->render();
}
8. Correct Use vs Incorrect Use
Correct Use
- Multiple related products must be used together.
- Product families must remain consistent.
- The system must switch families at runtime.
- Cross-platform frameworks.
- Theme engines (DarkThemeFactory, LightThemeFactory).
Incorrect Use
- Only one product type exists.
- No family relationship between objects.
- System unlikely to extend.
- Over-engineering small applications.
If you only need to create one object type, use Factory Method instead.
9. Advanced Topics
Abstract Factory with Templates
In performance-sensitive systems, virtual dispatch may be replaced with compile-time polymorphism using templates.
template<typename ButtonType, typename CheckboxType>
class StaticFactory {
public:
ButtonType createButton() { return ButtonType(); }
CheckboxType createCheckbox() { return CheckboxType(); }
};
This removes virtual overhead but sacrifices runtime flexibility.
Combining Abstract Factory with Dependency Injection
Instead of constructing factories inside main(), inject the factory into components:
class Application {
GUIFactory& factory;
public:
Application(GUIFactory& f) : factory(f) {}
void drawUI() {
auto btn = factory.createButton();
btn->paint();
}
};
This improves testability and modularity.
Plugin Architecture
Abstract Factory is often used in:
- Graphics engine backends
- Database drivers
- Compiler backends
- Game engine rendering layers
Each backend provides its own factory.
10. Abstract Factory vs. Factory Method (Common Confusion)
| Aspect | Abstract Factory | Factory Method |
|---|---|---|
| Purpose | Create families of related products | Let subclasses decide which single product to create |
| Scope | Object level (a factory object with multiple creation methods) | Method level (override a single creation method) |
| When to use | Multiple product types vary together (Button/Checkbox/…) | Need polymorphic creation of one product type |
| Client knowledge | Knows only factory & product interfaces | Knows a creator with an overridable method |
11. Summary
- Abstract Factory creates families of related objects.
- Ensures consistency across product variants.
- Improves scalability and architecture flexibility.
- It should be used when product families are required.
- Avoid when system complexity does not justify it.