Abstract Factory Pattern

What is it?
Why use it?
- You want consistent-looking components (like GUI widgets) that match a theme (e.g., Windows or Mac).
- You want to switch between product families without touching your app's main logic.
- You want your code to depend on interfaces, not concrete implementations.
Real-World Analogy
TypeScript Example – Cross-Platform UI
Step-by-step:
- Define abstract interfaces (Button, Checkbox)
- Implement concrete products (WinButton, MacButton, etc.)
- Create factory interface (GUIFactory)
- Implement concrete factories (WinFactory, MacFactory)
- Let application code use only the factory interface
Ts
// Abstract Product Interfaces
interface Button {
paint(): void;
}
interface Checkbox {
paint(): void;
}
// Concrete Products (Windows style)
class WinButton implements Button {
paint(): void {
console.log("Rendered a Windows-style button.");
}
}
class WinCheckbox implements Checkbox {
paint(): void {
console.log("Rendered a Windows-style checkbox.");
}
}
// Concrete Products (Mac style)
class MacButton implements Button {
paint(): void {
console.log("Rendered a Mac-style button.");
}
}
class MacCheckbox implements Checkbox {
paint(): void {
console.log("Rendered a Mac-style checkbox.");
}
}
// Abstract Factory
interface GUIFactory {
createButton(): Button;
createCheckbox(): Checkbox;
}
// Concrete Factory for Windows
class WinFactory implements GUIFactory {
createButton(): Button {
return new WinButton();
}
createCheckbox(): Checkbox {
return new WinCheckbox();
}
}
// Concrete Factory for Mac
class MacFactory implements GUIFactory {
createButton(): Button {
return new MacButton();
}
createCheckbox(): Checkbox {
return new MacCheckbox();
}
}
// Application that uses the factory
class Application {
private button: Button;
private checkbox: Checkbox;
constructor(factory: GUIFactory) {
this.button = factory.createButton();
this.checkbox = factory.createCheckbox();
}
paint(): void {
this.button.paint();
this.checkbox.paint();
}
}
// Simulate environment-based factory selection
function main(os: "Windows" | "Mac") {
let factory: GUIFactory;
if (os === "Windows") {
factory = new WinFactory();
} else if (os === "Mac") {
factory = new MacFactory();
} else {
throw new Error("Unsupported OS");
}
const app = new Application(factory);
app.paint();
}
main("Windows");
// Output:
// Rendered a Windows-style button.
// Rendered a Windows-style checkbox.
When to Use Abstract Factory
- Your app works with related objects that should be used together.
- You want to switch product families easily (e.g., light/dark themes, Windows/Mac).
- You want interface-based code with no dependency on concrete classes.
Pros & Cons
- Ensures product consistency (e.g., UI theme)
- Keeps code decoupled from specific implementations
- Easily extendable with new product families
- Can require a lot of extra classes and interfaces
- Might be overkill for small or unrelated object groups