Abstract Factory Pattern — Families of Related Objects
In this tutorial, you'll learn about Abstract Factory Pattern. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.
What You'll Learn
You will discover how the Abstract Factory pattern lets you compose systems from interchangeable product families, ensuring that objects from the same family work together seamlessly. You will see how it builds on the Factory Method pattern.
Why It Matters
Modern applications often need to support multiple themes, platforms, or data sources. Hardcoding these variants leads to sprawling conditional logic. Abstract Factory centralises the variant selection, so swapping an entire product family becomes a single configuration change. For example, switching your UI from a Windows look-and-feel to a macOS look-and-feel should not require editing every dialog — it should require changing one Factory choice at startup.
Real-World Use
Cross-platform UI toolkits (e.g., Qt, Java Swing) use Abstract Factory to produce buttons, text fields, and menus that match the operating system's look and feel. DodaTech's reporting engine uses an Abstract Factory to generate PDF, HTML, or Markdown reports from the same data model — selecting the Factory determines whether the output is a binary PDF, a styled HTML page, or plain Markdown, without the data model knowing about any of them.
The Pattern
The client uses an Abstract Factory interface. Concrete factories implement it to produce concrete products that belong together.
from abc import ABC, abstractmethod
class Button(ABC):
@abstractmethod
def paint(self) -> str:
pass
class Checkbox(ABC):
@abstractmethod
def paint(self) -> str:
pass
class WinButton(Button):
def paint(self) -> str:
return "Windows-style button"
class WinCheckbox(Checkbox):
def paint(self) -> str:
return "Windows-style checkbox"
class MacButton(Button):
def paint(self) -> str:
return "macOS-style button"
class MacCheckbox(Checkbox):
def paint(self) -> str:
return "macOS-style checkbox"
class GUIFactory(ABC):
@abstractmethod
def create_button(self) -> Button:
pass
@abstractmethod
def create_checkbox(self) -> Checkbox:
pass
class WinFactory(GUIFactory):
def create_button(self) -> Button:
return WinButton()
def create_checkbox(self) -> Checkbox:
return WinCheckbox()
class MacFactory(GUIFactory):
def create_button(self) -> Button:
return MacButton()
def create_checkbox(self) -> Checkbox:
return MacCheckbox()
def render_ui(factory: GUIFactory):
btn = factory.create_button()
chk = factory.create_checkbox()
print(btn.paint(), chk.paint())
render_ui(WinFactory())
render_ui(MacFactory())
Windows-style button Windows-style checkbox
macOS-style button macOS-style checkbox
Structure
classDiagram
class AbstractFactory {
+createProductA(): AbstractProductA
+createProductB(): AbstractProductB
}
class ConcreteFactory1 {
+createProductA(): ProductA1
+createProductB(): ProductB1
}
class ConcreteFactory2 {
+createProductA(): ProductA2
+createProductB(): ProductB2
}
class AbstractProductA
class AbstractProductB
class ProductA1
class ProductA2
class ProductB1
class ProductB2
ConcreteFactory1 --|> AbstractFactory
ConcreteFactory2 --|> AbstractFactory
ProductA1 ..|> AbstractProductA
ProductA2 ..|> AbstractProductA
ProductB1 ..|> AbstractProductB
ProductB2 ..|> AbstractProductB
ConcreteFactory1 --> ProductA1 : creates
ConcreteFactory1 --> ProductB1 : creates
ConcreteFactory2 --> ProductA2 : creates
ConcreteFactory2 --> ProductB2 : creates
Real-World Usage
- Java
javax.xml.parsers.DocumentBuilderFactory— creates SAX and DOM parsers through an Abstract Factory. - Python's
SQLAlchemydialects — each database backend has a Factory producing the correct SQL compiler, type compiler, and statement execution engine. - AWS SDK credential providers — Abstract Factory pattern supplies credentials from environment variables, IAM roles, or profile files.
Related Patterns
- Factory Method is often used within Abstract Factory to create individual products.
- Singleton often holds the concrete factory instance.
- Builder separates complex construction from representation, complementing Abstract Factory.
Pros and Cons
| Pros | Cons |
|---|---|
| Ensures product consistency within a family | Adding new products requires changing the Factory interface |
| Isolates concrete classes from clients | Can introduce unnecessary complexity for simple hierarchies |
| Swapping product families is trivial | Overhead of creating and maintaining Factory classes |
| Follows the Open/Closed Principle at the family level | May not suit systems with few product types |
The render_ui function accepts any GUIFactory and uses it to create both a button and a checkbox. The critical guarantee is that a WinFactory always creates matching Windows-style components, and a MacFactory always creates matching macOS-style components. This consistency across product families is the pattern's primary value.
Practice Questions
- How would you add a new product type (e.g., Slider) to an existing Abstract Factory without breaking existing concrete factories?
- Compare Abstract Factory with passing Factory Method references around — when does one become preferable over the other?
- Implement a Factory registry that selects the correct concrete Factory based on a runtime configuration string.
- How does the Abstract Factory pattern interact with the Dependency Inversion Principle?
Challenge
Add a third product family — Linux GTK-style components — by creating LinuxButton, LinuxCheckbox, and LinuxFactory. Verify that render_ui works unchanged with your new Factory.
Real-World Task
Identify a part of your application that creates related groups of objects (e.g., different data access handlers for different storage backends). Refactor it to use Abstract Factory and use DodaTech's architecture diff tool to compare the before and after dependency graphs.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro