Chapter 03 · Article 12 of 55
Abstract Factory Pattern
Intent: Provide an interface for creating families of related or dependent objects without specifying their concrete classes.
Article outline14 sections on this page+
Overview
Intent: Provide an interface for creating families of related or dependent objects without specifying their concrete classes.
The Abstract Factory pattern is a creational design pattern that encapsulates a group of individual factories sharing a common theme. The client code works with factories and products through abstract interfaces, making it independent of the actual concrete classes being instantiated.
Unlike a single factory that produces one type of object, an Abstract Factory produces an entire family of related products. The key guarantee is consistency within a family - if you use a Windows factory, every widget it produces will be a Windows-style widget. You never accidentally mix a macOS button with a Windows checkbox.
Also Known As: Kit
Pattern Type: Creational (Object)
Complexity: ** (Moderate)
Problem It Solves
Consider building a cross-platform desktop application. Your UI layer needs buttons, checkboxes, text fields, and scroll bars. Each platform (Windows, macOS, Linux) has its own look-and-feel for these widgets. Without Abstract Factory, you face several problems:
-
Inconsistent object families - Scattered
if/elseorswitchblocks throughout the codebase risk mixing platform-specific widgets. A single missed condition means a macOS checkbox appears in a Windows-themed window. -
Tight coupling to concrete classes - Client code that directly instantiates
WindowsButtonorMacCheckboxbecomes impossible to port or extend without modifying every instantiation site. -
Violation of the Open/Closed Principle for families - Adding a new platform (e.g., Linux) requires hunting through every creation point rather than adding a single new factory class.
-
Theme/skin systems - Applications supporting light/dark themes or brand-specific styling need a mechanism to swap entire visual families at runtime without rewriting rendering logic.
-
Database abstraction layers - Connecting to MySQL vs. PostgreSQL requires consistent families of connections, commands, readers, and adapters that must not be mixed.
The Abstract Factory solves these by centralizing family creation behind a single interface, guaranteeing that all products from one factory are compatible with each other.
When to Use / When NOT to Use
| When to Use | When NOT to Use |
|---|---|
| System must be independent of how products are created and composed | You only need to create a single type of object (use Factory Method instead) |
| System must work with multiple families of related products | Product families are unlikely to change or expand |
| You need to enforce consistency among related objects | The added abstraction layers introduce unnecessary complexity |
| Product families are designed to be used together exclusively | Objects in the "family" are actually independent and don't need consistency |
| You want to provide a library of products revealing only interfaces | You have only one concrete family and no foreseeable need for more |
| Runtime switching between product families is required (themes, platforms) | Performance-critical paths where virtual dispatch overhead matters |
| You want to isolate concrete classes from client code | The number of product types changes frequently (each addition modifies all factories) |
Key Concepts & Theory
Factory of Factories
The Abstract Factory is sometimes described as a "factory of factories." More precisely, it is a super-factory that defines the interface for creating each product in a family. Each concrete factory implements this interface to produce platform-specific or theme-specific variants.
Product Families
A product family is a set of related objects designed to work together. For example:
- Windows family: WindowsButton, WindowsCheckbox, WindowsTextField
- Mac family: MacButton, MacCheckbox, MacTextField
The Abstract Factory guarantees that if you choose the Windows factory, you get all Windows products - never a mix.
Consistency Guarantee
The pattern's primary value proposition is intra-family consistency. Because a single factory object produces all related products, it is structurally impossible for client code to accidentally combine incompatible objects. This is enforced at compile time through the type system.
Platform Abstraction
Client code programs against abstract product interfaces (Button, Checkbox) and the abstract factory interface (GUIFactory). The concrete platform is selected once - typically at application startup via configuration or environment detection - and injected into the client. The client never knows which concrete family it is using.
Dependency Inversion
The pattern is a textbook application of the Dependency Inversion Principle: high-level modules (client/application logic) depend on abstractions (abstract factory + abstract products), not on low-level modules (concrete factories + concrete products).
ASCII Class Diagram
┌─────────────────────────────────┐
│ <<interface>> │
│ AbstractFactory │
├─────────────────────────────────┤
│ + createProductA(): AbstractProductA │
│ + createProductB(): AbstractProductB │
└──────────┬──────────┬───────────┘
│ │
┌─────┘ └─────┐
│ │
▼ ▼
┌──────────────┐ ┌──────────────┐
│ConcreteFactory1│ │ConcreteFactory2│
├──────────────┤ ├──────────────┤
│+createProductA()│ │+createProductA()│
│+createProductB()│ │+createProductB()│
└──────┬───────┘ └──────┬───────┘
│ creates │ creates
▼ ▼
┌──────────────┐ ┌──────────────┐
│ConcreteProductA1│ │ConcreteProductA2│
└──────────────┘ └──────────────┘
┌──────────────┐ ┌──────────────┐
│ConcreteProductB1│ │ConcreteProductB2│
└──────────────┘ └──────────────┘
▲ ▲
│ implements │ implements
│ │
┌──────────────────┐ ┌──────────────────┐
│ <<interface>> │ │ <<interface>> │
│ AbstractProductA │ │ AbstractProductB │
├──────────────────┤ ├──────────────────┤
│ + operationA() │ │ + operationB() │
└──────────────────┘ └──────────────────┘
Client ──────► AbstractFactory
Client ──────► AbstractProductA
Client ──────► AbstractProductB
(Client depends ONLY on abstractions)
Pseudocode Implementation
Abstract Products
interface Button {
method render()
method onClick(handler)
}
interface Checkbox {
method render()
method toggle()
method isChecked(): boolean
}
interface TextField {
method render()
method getValue(): string
method setValue(text: string)
}
Abstract Factory
interface GUIFactory {
method createButton(label: string): Button
method createCheckbox(label: string): Checkbox
method createTextField(placeholder: string): TextField
}
Concrete Products - Windows Family
class WindowsButton implements Button {
method render() {
// Draw button with Windows flat style, Segoe UI font
drawRect(style: "win-flat", font: "Segoe UI")
}
method onClick(handler) {
registerWin32ClickEvent(handler)
}
}
class WindowsCheckbox implements Checkbox {
field checked = false
method render() {
// Draw square checkbox with checkmark, Windows style
drawWinCheckbox(checked: this.checked)
}
method toggle() { this.checked = !this.checked }
method isChecked() { return this.checked }
}
class WindowsTextField implements TextField {
field text = ""
method render() {
// Draw single-line edit control, Windows style
drawWinEditControl(placeholder: this.placeholder)
}
method getValue() { return this.text }
method setValue(t) { this.text = t }
}
Concrete Products - Mac Family
class MacButton implements Button {
method render() {
// Draw button with macOS rounded style, SF Pro font
drawRoundedRect(style: "aqua", font: "SF Pro")
}
method onClick(handler) {
registerCocoaAction(handler)
}
}
class MacCheckbox implements Checkbox {
field checked = false
method render() {
// Draw rounded checkbox with blue tint, macOS style
drawMacCheckbox(checked: this.checked)
}
method toggle() { this.checked = !this.checked }
method isChecked() { return this.checked }
}
class MacTextField implements TextField {
field text = ""
method render() {
// Draw NSTextField-style input, macOS style
drawMacTextField(placeholder: this.placeholder)
}
method getValue() { return this.text }
method setValue(t) { this.text = t }
}
Concrete Factories
class WindowsFactory implements GUIFactory {
method createButton(label) {
return new WindowsButton(label)
}
method createCheckbox(label) {
return new WindowsCheckbox(label)
}
method createTextField(placeholder) {
return new WindowsTextField(placeholder)
}
}
class MacFactory implements GUIFactory {
method createButton(label) {
return new MacButton(label)
}
method createCheckbox(label) {
return new MacCheckbox(label)
}
method createTextField(placeholder) {
return new MacTextField(placeholder)
}
}
Client Code
class SettingsDialog {
field factory: GUIFactory
field saveButton: Button
field darkModeCheckbox: Checkbox
field usernameField: TextField
constructor(factory: GUIFactory) {
this.factory = factory
}
method initialize() {
this.saveButton = factory.createButton("Save")
this.darkModeCheckbox = factory.createCheckbox("Dark Mode")
this.usernameField = factory.createTextField("Enter username...")
}
method render() {
this.usernameField.render()
this.darkModeCheckbox.render()
this.saveButton.render()
this.saveButton.onClick(() => this.save())
}
method save() {
username = this.usernameField.getValue()
darkMode = this.darkModeCheckbox.isChecked()
// persist settings...
}
}
// --- Application Bootstrap ---
method main() {
config = readConfig()
factory: GUIFactory
if config.os == "windows" {
factory = new WindowsFactory()
} else if config.os == "mac" {
factory = new MacFactory()
}
dialog = new SettingsDialog(factory)
dialog.initialize()
dialog.render()
}
The client (SettingsDialog) has zero knowledge of Windows or Mac classes. Swapping platforms requires changing only the factory instantiation at the bootstrap level.
Abstract Factory vs Factory Method
| Aspect | Factory Method | Abstract Factory |
|---|---|---|
| Scope | Creates a single product | Creates families of related products |
| Abstraction Level | One method in a class defers instantiation to subclasses | An entire object dedicated to creating a product family |
| Number of Products | One product per factory method | Multiple products per factory |
| Implementation | Uses inheritance (subclass overrides creation method) | Uses composition (client holds a factory object) |
| Complexity | Low - single method override | Moderate - interface with multiple creation methods |
| Consistency Guarantee | None across products | Guarantees all products belong to same family |
| Adding New Products | Add a new factory method (easy) | Must modify the abstract factory interface (hard) |
| Adding New Families | Create a new subclass | Create a new concrete factory (easy) |
| Client Coupling | Client may still know the creator subclass | Client knows only the abstract factory interface |
| Typical Use Case | Framework hook for object creation | Platform/theme/brand abstraction |
| GoF Classification | Class-level creational | Object-level creational |
| Relationship | Abstract Factory often uses Factory Methods internally | Abstract Factory is a higher-level pattern |
Rule of thumb: If you need one product, start with Factory Method. If you need a consistent set of products, graduate to Abstract Factory.
Real-World Examples
1. Cross-Platform UI Toolkits
Qt Framework uses an abstract factory approach internally. QPlatformTheme and QPlatformIntegration serve as abstract factories that produce platform-specific dialogs, fonts, icons, and system tray implementations. Each platform plugin (xcb, cocoa, windows) provides concrete factories.
Java AWT/Swing - java.awt.Toolkit is an abstract factory. Toolkit.getDefaultToolkit() returns a platform-specific toolkit (WToolkit on Windows, XToolkit on Linux) that creates native peers for every AWT component.
2. Database Driver Families
ADO.NET's DbProviderFactory is a classic Abstract Factory:
SqlClientFactory→ createsSqlConnection,SqlCommand,SqlDataReaderOracleClientFactory→ createsOracleConnection,OracleCommand,OracleDataReader
Each factory produces a consistent family of database objects. Client code works with DbConnection, DbCommand, etc., never knowing the concrete database.
3. Document Format Families
A document export system might define:
PDFFactory→ createsPDFHeading,PDFParagraph,PDFTable,PDFImageHTMLFactory→ createsHTMLHeading,HTMLParagraph,HTMLTable,HTMLImageMarkdownFactory→ createsMDHeading,MDParagraph,MDTable,MDImage
The document builder works with abstract DocumentElement interfaces, and the chosen factory ensures all elements render in the same format.
4. Cloud Provider Abstraction
Multi-cloud libraries abstract away provider differences:
AWSFactory→ createsS3Storage,SQSQueue,LambdaFunctionGCPFactory→ createsGCSStorage,PubSubQueue,CloudFunction
Advantages & Disadvantages
| Advantages | Disadvantages |
|---|---|
| Isolation of concrete classes - Client never references concrete product names | Difficulty adding new product types - Adding a new product requires changing the abstract factory interface and ALL concrete factories |
| Consistency guarantee - Products from one factory are always compatible | Increased complexity - Many new interfaces and classes for each family |
| Easy family swapping - Change one factory object to switch entire product family | Overkill for simple cases - If you have one family or one product, the pattern adds unnecessary abstraction |
| Single Responsibility - Product creation code is centralized in one place | Rigid interface - The abstract factory fixes the set of products at design time |
| Open/Closed for new families - Adding a new platform means adding one factory class | Explosion of classes - N families × M products = N×M concrete product classes + N factory classes |
| Promotes programming to interfaces - Enforces dependency inversion naturally | Can obscure code flow - Indirection makes it harder to trace which concrete class is instantiated |
| Testability - Inject mock factories for unit testing | Performance overhead - Virtual dispatch and object allocation for every product |
Constraints & Edge Cases
Adding New Product Types Violates OCP
The most significant constraint: if you add a new product (e.g., Slider) to the abstract factory interface, every concrete factory must be updated. This violates the Open/Closed Principle along the product-type axis. Mitigation strategies:
- Use a parameterized factory method with a product-type enum (sacrifices type safety)
- Use the Prototype pattern - factories store prototypical instances and clone them
- Accept the trade-off: Abstract Factory is open for new families but closed for new product types
Complexity Overhead
For systems with only 1-2 product types, Abstract Factory introduces unnecessary indirection. A simple Factory Method or even direct construction with dependency injection may suffice. Apply the pattern when you have ≥3 related products AND ≥2 families.
When Factory Method Suffices
If products don't need to be consistent with each other - e.g., you create buttons independently of checkboxes - Factory Method per product type is simpler and more flexible.
Singleton Factories
Concrete factories are often implemented as Singletons since they carry no mutable state. This is acceptable but be cautious in multi-threaded environments if factories cache products.
Lazy vs. Eager Creation
Some implementations create all products upfront (eager), while others create on demand (lazy). Lazy creation is preferred for expensive objects but complicates error handling - a creation failure mid-workflow leaves the system in a partially initialized state.
Serialization Boundaries
When products cross serialization boundaries (network, disk), the receiving side needs the correct factory to reconstruct objects. Pass a factory identifier alongside serialized data.
Interview Follow-ups
Q1: How does Abstract Factory differ from Builder pattern?
A: Abstract Factory focuses on what family of objects to create (the product selection), while Builder focuses on how to construct a complex object step by step. Abstract Factory returns products immediately; Builder accumulates construction steps and returns the final product at the end. They can be combined: a Builder might use an Abstract Factory to obtain the parts it assembles.
Q2: How would you add a new product type without modifying existing factories?
A: Three approaches: (1) Use a default method in the abstract factory interface (languages supporting this) that throws UnsupportedOperationException - existing factories compile but fail at runtime for the new product. (2) Create a separate, parallel factory interface for the new product and compose it with the existing factory. (3) Use the Extension Interface pattern - define optional product creation in a separate interface that only some factories implement, and have clients check with instanceof before calling.
Q3: Can Abstract Factory work with Dependency Injection frameworks?
A: Yes, and it's a natural fit. The DI container acts as the bootstrap that selects and injects the correct concrete factory based on configuration. Spring's @Profile annotation, for example, can activate a WindowsFactory bean in Windows environments and a MacFactory bean on macOS. The factory itself is injected into client classes, preserving the pattern's decoupling benefits while eliminating manual if/else selection logic.
Q4 (Hint Only): How would you implement Abstract Factory in a language without interfaces (e.g., C)?
Hint: Think about structs containing function pointers. Each "concrete factory" is a struct instance with its function pointer fields assigned to platform-specific creation functions. The client receives a pointer to this struct and calls through the function pointers.
Q5 (Hint Only): How does Abstract Factory interact with the Prototype pattern to solve the "new product type" problem?
Hint: Instead of a fixed set of creation methods, the factory stores a registry (map) of product-type keys to prototypical instances. Creating a product means looking up the prototype and cloning it. Adding a new product type means registering a new prototype - no interface change required.
Counter Questions to Ask Interviewer
-
"How many product families do we anticipate?" - If only one family exists today with no foreseeable second, Abstract Factory is premature. Understanding growth expectations determines whether the pattern's overhead is justified.
-
"Do all products in a family need to be consistent, or can they be mixed?" - If mixing is acceptable (e.g., a Windows button with a custom-styled checkbox), the consistency guarantee is unnecessary and Factory Method per product is simpler.
-
"How frequently do new product types get added vs. new families?" - Abstract Factory excels when new families are added often but product types are stable. If product types change frequently, the pattern becomes a maintenance burden.
-
"Is runtime family switching required, or is the family fixed at startup?" - Runtime switching (e.g., live theme changes) requires the factory to be swappable, which influences whether products hold back-references to their factory.
-
"What's the deployment model - monolith or microservices?" - In microservices, each service might only need one family, making the abstraction unnecessary within a single service. The pattern may still apply at the API gateway or SDK level.
References & Whitepapers
-
Gamma, E., Helm, R., Johnson, R., Vlissides, J. - Design Patterns: Elements of Reusable Object-Oriented Software (1994), Chapter 3: Creational Patterns - Abstract Factory. The foundational reference defining the pattern, its structure, participants, and consequences.
-
Qt Framework Architecture - Qt's platform abstraction layer (
QPlatformIntegration,QPlatformTheme) demonstrates industrial-scale Abstract Factory usage. Each platform plugin implements these interfaces to produce native widgets, dialogs, and system services. See: Qt Platform Abstraction -
Freeman, E., Robson, E. - Head First Design Patterns (2004), Chapter 4. Provides an accessible pizza-store analogy for Abstract Factory with Java examples.
-
Martin, R.C. - Agile Software Development: Principles, Patterns, and Practices (2002). Discusses Abstract Factory in the context of the Dependency Inversion Principle and package design.
-
Microsoft ADO.NET Documentation -
DbProviderFactoryclass hierarchy. A production implementation of Abstract Factory for database-agnostic data access. See: DbProviderFactory Class -
Bloch, J. - Effective Java (3rd Edition, 2018), Item 64: "Refer to objects by their interfaces." Reinforces the principle that Abstract Factory leverages.
Related Topics
- Factory Method - Single-product creation via inheritance; often used internally by Abstract Factory
- Builder Pattern - Step-by-step construction of complex objects; complementary to Abstract Factory
- Prototype Pattern - Clone-based creation; alternative approach to avoiding the "new product type" problem
- Singleton Pattern - Concrete factories are often implemented as Singletons
- Dependency Injection - Modern alternative/complement that achieves similar decoupling
- Bridge Pattern - Separates abstraction from implementation; often used alongside Abstract Factory
- Strategy Pattern - Abstract Factory can be seen as a Strategy for object creation