design patterns
Gang of Four Design Patterns
The 23 classic object-oriented design patterns from the Gang of Four, organized by purpose with practical examples.
Gang of Four Design Patterns
TL;DR
The 23 Gang of Four (GoF) patterns are reusable solutions to common object-oriented design problems, organized into three categories: Creational (object creation), Structural (composition), and Behavioral (interaction). Know the patterns, but apply them only when they solve a real problem—not to demonstrate cleverness.
Key Takeaways
- Creational patterns abstract object instantiation (Factory, Singleton, Builder)
- Structural patterns compose objects into larger structures (Adapter, Facade, Decorator)
- Behavioral patterns define object interaction (Strategy, Observer, Command)
- Patterns have trade-offs: added abstraction vs flexibility
- Intent matters: understand the problem before applying the pattern
Why This Matters
Design patterns provide a shared vocabulary for discussing design decisions. When a developer says "use the Strategy pattern," everyone understands the approach without lengthy explanation. However, patterns are tools, not goals. Forcing patterns into code that doesn't need them creates unnecessary complexity. The best code is often the simplest code that solves the problem.
Pattern Abuse
The most common mistake is applying patterns prematurely. If you're adding a Factory for a class that will never have multiple implementations, you're adding complexity without benefit.
Pattern Catalog Overview
GANG OF FOUR PATTERNS (23)
├── CREATIONAL (5): Object creation mechanisms
│ ├── Factory Method
│ ├── Abstract Factory
│ ├── Builder
│ ├── Prototype
│ └── Singleton
│
├── STRUCTURAL (7): Object composition
│ ├── Adapter
│ ├── Bridge
│ ├── Composite
│ ├── Decorator
│ ├── Facade
│ ├── Flyweight
│ └── Proxy
│
└── BEHAVIORAL (11): Object interaction
├── Chain of Responsibility
├── Command
├── Interpreter
├── Iterator
├── Mediator
├── Memento
├── Observer
├── State
├── Strategy
├── Template Method
└── Visitor
Creational Patterns
Intent
Define an interface for creating objects, letting subclasses decide which class to instantiate.
Structure
┌────────────────┐ ┌────────────────┐
│ Creator │ │ Product │
├────────────────┤ │ (interface) │
│ factoryMethod()│ └────────────────┘
│ operation() │ ▲
└───────┬────────┘ │
│ ┌──────┴──────┐
▼ │ │
┌────────────────┐ ┌─────────┐ ┌─────────┐
│ConcreteCreator │ │ProductA │ │ProductB │
├────────────────┤ └─────────┘ └─────────┘
│ factoryMethod()│
└────────────────┘
When to Use
- Class can't anticipate the type of objects it needs to create
- Class wants subclasses to specify the objects it creates
- You want to localize the knowledge of which class gets created
Example
// Product interface
interface Logger {
log(message: string): void;
}
// Concrete products
class ConsoleLogger implements Logger {
log(message: string) { console.log(message); }
}
class FileLogger implements Logger {
log(message: string) { /* write to file */ }
}
// Creator with factory method
abstract class Application {
abstract createLogger(): Logger;
run() {
const logger = this.createLogger();
logger.log("Application started");
}
}
// Concrete creators
class DevelopmentApp extends Application {
createLogger() { return new ConsoleLogger(); }
}
class ProductionApp extends Application {
createLogger() { return new FileLogger(); }
}Structural Patterns
Intent
Convert an interface into another interface that clients expect.
Structure
┌────────┐ ┌─────────────┐ ┌─────────────┐
│ Client │────→│ Target │ │ Adaptee │
└────────┘ │ (interface) │ │(existing) │
└──────┬──────┘ └──────┬──────┘
│ │
▼ │
┌─────────────┐ │
│ Adapter │────────────┘
└─────────────┘
When to Use
- Use an existing class with an incompatible interface
- Create a reusable class that works with unrelated classes
- Need to use several existing subclasses without subclassing each
Example
// Existing class (third-party library)
class LegacyPrinter {
printText(text: string) {
console.log(`Printing: ${text}`);
}
}
// Target interface your code expects
interface ModernPrinter {
print(document: Document): void;
}
// Adapter
class PrinterAdapter implements ModernPrinter {
constructor(private legacyPrinter: LegacyPrinter) {}
print(document: Document) {
const text = document.toString();
this.legacyPrinter.printText(text);
}
}Behavioral Patterns
Intent
Define a family of algorithms, encapsulate each one, and make them interchangeable.
Structure
┌─────────┐ ┌────────────────┐
│ Context │─────→│ Strategy │
└─────────┘ │ (interface) │
└───────┬────────┘
│
┌──────────────┼──────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│StrategyA │ │StrategyB │ │StrategyC │
└──────────┘ └──────────┘ └──────────┘
When to Use
- Many related classes differ only in behavior
- Need different variants of an algorithm
- Avoid exposing complex algorithm-specific data structures
- Class has multiple conditional behaviors
Example
interface PaymentStrategy {
pay(amount: number): void;
}
class CreditCardPayment implements PaymentStrategy {
constructor(private cardNumber: string) {}
pay(amount: number) {
console.log(`Paid $${amount} with credit card`);
}
}
class PayPalPayment implements PaymentStrategy {
constructor(private email: string) {}
pay(amount: number) {
console.log(`Paid $${amount} via PayPal`);
}
}
class ShoppingCart {
constructor(private paymentStrategy: PaymentStrategy) {}
checkout(amount: number) {
this.paymentStrategy.pay(amount);
}
setPaymentStrategy(strategy: PaymentStrategy) {
this.paymentStrategy = strategy;
}
}
// Usage
const cart = new ShoppingCart(new CreditCardPayment("1234..."));
cart.checkout(100);
cart.setPaymentStrategy(new PayPalPayment("user@email.com"));
cart.checkout(50);Pattern Selection Guide
| Feature | Problem | Consider | Why |
|---|---|---|---|
| Complex object creation | Builder, Factory | Separate construction from representation | |
| Family of related objects | Abstract Factory | Ensure compatibility | |
| Incompatible interfaces | Adapter | Convert interface | |
| Simplify complex subsystem | Facade | Unified interface | |
| Add behavior dynamically | Decorator | Avoid subclass explosion | |
| Control access to object | Proxy | Lazy load, security, caching | |
| Varying algorithms | Strategy | Encapsulate algorithm family | |
| React to state changes | Observer | Loose coupling | |
| State-dependent behavior | State | State machine | |
| Undo/redo operations | Command | Encapsulate requests |
Quick Reference Card
┌─────────────────────────────────────────────────────────────┐
│ GANG OF FOUR PATTERNS CHEAT SHEET │
├─────────────────────────────────────────────────────────────┤
│ │
│ CREATIONAL │
│ ───────────────────────────────────────────────────────── │
│ Factory Method → Subclass decides which class to create │
│ Abstract Factory→ Create families of related objects │
│ Builder → Complex construction step by step │
│ Prototype → Clone existing objects │
│ Singleton → Single instance (use sparingly!) │
│ │
│ STRUCTURAL │
│ ───────────────────────────────────────────────────────── │
│ Adapter → Convert interface to expected interface │
│ Bridge → Separate abstraction from implementation │
│ Composite → Tree structures, uniform treatment │
│ Decorator → Add responsibilities dynamically │
│ Facade → Simplified interface to subsystem │
│ Flyweight → Share common state across objects │
│ Proxy → Placeholder controlling access │
│ │
│ BEHAVIORAL │
│ ───────────────────────────────────────────────────────── │
│ Chain of Resp. → Pass request along chain of handlers │
│ Command → Encapsulate request as object │
│ Iterator → Sequential access without exposure │
│ Mediator → Centralize complex communication │
│ Memento → Capture and restore state │
│ Observer → Notify dependents of state changes │
│ State → Behavior changes with state │
│ Strategy → Interchangeable algorithms │
│ Template Method → Algorithm skeleton, steps in subclasses │
│ Visitor → New operations without changing classes │
│ │
├─────────────────────────────────────────────────────────────┤
│ RULE: Use patterns to solve problems, not to show off. │
└─────────────────────────────────────────────────────────────┘
Related Topics
- Microservices Patterns - Distributed system patterns
- SOLID Principles - Design principles underlying patterns
- Quality Attributes - How patterns affect system qualities
Sources
- Design Patterns: Elements of Reusable OO Software - Gamma, Helm, Johnson, Vlissides (GoF)
- Refactoring Guru - Design Patterns - Visual explanations
- Head First Design Patterns - Accessible introduction