On this page
Description
Abstract Factory is the creational pattern for families of objects that must match - all dark-theme widgets or all light-theme ones, all PostgreSQL objects or all MySQL ones. Instead of deciding "which family?" every time an object is created, you choose one factory once, and everything it creates belongs to the same family. This skill covers the pattern, how it relates to Factory Method, and the one kind of change it makes expensive.
A UI supports a dark and a light theme, and every screen builds its own widgets:
Java
Button ok = dark ? new DarkButton() : new LightButton();
Checkbox remember = dark ? new DarkCheckbox() : new LightCheckbox();
TextField name = new LightTextField(); // someone forgot the check
Python
ok = DarkButton() if dark else LightButton()
remember = DarkCheckbox() if dark else LightCheckbox()
name = LightTextField() # someone forgot the check
Every widget repeats the same decision, and one forgotten check puts a light text field on a dark screen. A choice that should be made once - "which theme?" - is made dozens of times, separately for each product.
We'll go from simple to more technical:
- The pattern: one factory object with a creation method per product.
- Where you'll see it: UI toolkits, database drivers, environment-specific setups.
- Which changes it makes cheap (a new family) and which it makes expensive (a new product).
- Choosing among the creational patterns.
The pattern
Java
interface UIFactory {
Button createButton();
Checkbox createCheckbox();
}
class WindowsUIFactory implements UIFactory { /* ... */ }
class MacUIFactory implements UIFactory { /* ... */ }
// caller picks ONE factory instance, then every widget it produces matches:
UIFactory factory = isMac ? new MacUIFactory() : new WindowsUIFactory();
Button b = factory.createButton();
Checkbox c = factory.createCheckbox(); // guaranteed same theme as b
Python
from abc import ABC, abstractmethod
class UIFactory(ABC):
@abstractmethod
def create_button(self) -> Button: ...
@abstractmethod
def create_checkbox(self) -> Checkbox: ...
class WindowsUIFactory(UIFactory): ...
class MacUIFactory(UIFactory): ...
# caller picks ONE factory instance, then every widget it produces matches:
factory = MacUIFactory() if is_mac else WindowsUIFactory()
b = factory.create_button()
c = factory.create_checkbox() # guaranteed same theme as b
Each createX() method is itself a factory method. Abstract Factory groups them on one object, so the whole family is chosen by a single decision - which factory you hand out.
Where you'll see it
Cross-platform UI toolkits and database-vendor abstraction layers (a factory that produces a matched Connection, Statement, and ResultSet implementation set for whichever vendor is configured) both need this "everything from one factory is guaranteed compatible" property. In Python, a DB-API driver module plays that role: the connection psycopg.connect() returns makes cursors of its own family, and code written against the DB-API works with any driver.
Choosing among the creational patterns
| If you need... | Reach for | Because |
|---|---|---|
| Exactly one shared instance, by design | Singleton | Two copies would be a bug (e.g. two connection pools fighting over the same database connections). |
| One place that decides which concrete class to build | Factory Method | Callers depend on an interface; adding a type changes the factory, not every caller. |
| A matched family of related objects that must vary together | Abstract Factory (this lesson) | Swapping the factory swaps the whole family, so mismatched pieces can't be combined. |
| An object with many optional parts, assembled step by step | Builder | Named, optional steps instead of long positional constructors; validate once in build(). |
| Many near-identical objects that are expensive to set up | Prototype | Copy a ready-made template instead of repeating costly construction. |
Common mistakes
| Mistake | What happens | Do this instead |
|---|---|---|
| An Abstract Factory for a single product type | a "family" of one - extra structure, no benefit | a simple factory or Factory Method |
| Choosing the family in many places | the mismatch bug comes back | choose the factory once, at startup or from configuration |
| Creating two factories and mixing their products | a dark button on a light screen | pass one factory around; let code ask only it |
| Expecting new products to be cheap | every factory must implement the new method | expect it, or give the new method a default |
| Hand-written factory hierarchies duplicating DI profiles | two mechanisms for one choice | let the container select the family per environment |
Check yourself
Try to answer before opening each one - recalling beats re-reading.
1. You add a third theme (high contrast), and later a new widget (Slider). What changes each time?
Show answer
The theme: one new factory class, nothing else. The widget: the factory interface gains createSlider(), and every existing factory must implement it.
2. The app only has buttons - no checkboxes, no other widgets. Abstract Factory?
Show answer
No. With one product there is no family to keep consistent; a simple factory (or no factory at all) is enough.
3. Where does the "which theme?" decision live once you use an Abstract Factory?
Show answer
In one place: wherever the factory is chosen, often at startup or from configuration. Every screen just uses the factory it is given.
Key takeaways
- It creates families of related objects that must match (all dark-theme, or all light-theme).
- Client code talks only to the factory and product interfaces, so it cannot mix families by accident.
- Adding a new family is easy (one new factory class); adding a new product type touches every factory.
- With only one product type there is no family to protect - a plain Factory Method is enough.