Design Patterns

Design Patterns

DesignSoftwareArchitectureProgramming

The Ultimate Guide to Design Patterns

1. From Zero to Hero: Understanding Design Patterns Through Analogies, Examples & Code


2. Table of Contents

  1. What Are Design Patterns?
  2. Why Should You Care?
  3. Creational Patterns (5 patterns)
  4. Structural Patterns (7 patterns)
  5. Behavioral Patterns (11 patterns)
  6. Additional Modern Patterns
  7. Quick Reference & Mnemonics

3. What Are Design Patterns?

Design patterns are reusable solutions to commonly occurring problems in software design. They're not finished code you can copy-paste — they're templates, blueprints, or recipes that guide you in solving specific design challenges.

Think of them like architectural blueprints: just as architects don't redesign doors and windows for every building, software developers don't need to reinvent solutions for recurring problems.

3.1. The Gang of Four (GoF)

In 1994, four authors — Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides — published "Design Patterns: Elements of Reusable Object-Oriented Software." This book cataloged 23 patterns that have become the foundation of modern software design.


4. Why Should You Care?

4.1. Without Design Patterns:

  • 🔴 Code becomes tangled spaghetti over time
  • 🔴 Every developer solves the same problem differently
  • 🔴 Maintenance nightmares — changing one thing breaks ten others
  • 🔴 Onboarding new team members takes forever
  • 🔴 Reinventing wheels constantly

4.2. With Design Patterns:

  • 🟢 Common vocabulary across teams and projects
  • 🟢 Proven solutions backed by decades of use
  • 🟢 Flexible, maintainable, scalable code
  • 🟢 Faster development through recognized templates
  • 🟢 Easier code reviews and documentation

5. Rating System

Each pattern is rated on:

MetricDescription
ComplexityHow difficult to understand and implement (★☆☆☆☆ = Simple, ★★★★★ = Complex)
FrequencyHow often you'll use this in real projects (★☆☆☆☆ = Rare, ★★★★★ = Very Common)

Creational Patterns

Mnemonic: "Smart Factories Always Build Prototypes"

Singleton, Factory Method, Abstract Factory, Builder, Prototype

Creational patterns deal with object creation mechanisms — trying to create objects in a manner suitable to the situation, rather than using basic instantiation.


6. 1. Singleton

Complexity: ★★☆☆☆ | Frequency: ★★★★★

6.1. Definition

Singleton ensures a class has only one instance and provides a global point of access to it.

6.2. Why Is It Required?

Some resources should exist exactly once: a database connection pool, a logger, a configuration manager, or a game's audio system. Creating multiple instances would waste resources, cause conflicts, or produce inconsistent behavior.

6.3. With vs Without

Without SingletonWith Singleton
Multiple database connections fighting for resourcesSingle shared connection pool
Inconsistent configuration across app modulesOne source of truth for settings
Multiple loggers writing to same file = corruptionUnified logging system

6.4. Best Use Cases

  • Web Dev: Application configuration, logging service, database connection pool
  • Game Dev: Audio manager, game state manager, input handler
  • Enterprise: License manager, thread pool, cache manager

6.5. Analogy

The President of a Country — There can only be one president at a time. No matter who asks "Who is the president?", they get the same person. You can't just create a new president; you access the existing one through official channels.

6.6. Code Examples

Python:

class DatabaseConnection:
    _instance = None
    
    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
            cls._instance.connection = cls._connect_to_db()
        return cls._instance
    
    @staticmethod
    def _connect_to_db():
        print("Establishing database connection...")
        return {"host": "localhost", "port": 5432}

# Usage
db1 = DatabaseConnection()
db2 = DatabaseConnection()
print(db1 is db2)  # True — same instance!

C++:

#include <iostream>
#include <mutex>

class DatabaseConnection {
private:
    static DatabaseConnection* instance;
    static std::mutex mutex;
    
    DatabaseConnection() {
        std::cout << "Establishing database connection..." << std::endl;
    }

public:
    DatabaseConnection(const DatabaseConnection&) = delete;
    
    static DatabaseConnection* getInstance() {
        std::lock_guard<std::mutex> lock(mutex);
        if (instance == nullptr) {
            instance = new DatabaseConnection();
        }
        return instance;
    }
};

DatabaseConnection* DatabaseConnection::instance = nullptr;
std::mutex DatabaseConnection::mutex;

JavaScript:

class DatabaseConnection {
    constructor() {
        if (DatabaseConnection.instance) {
            return DatabaseConnection.instance;
        }
        this.connection = { host: "localhost", port: 5432 };
        DatabaseConnection.instance = this;
    }
}

const db1 = new DatabaseConnection();
const db2 = new DatabaseConnection();
console.log(db1 === db2);  // true
AnalogyUML DiagramMinimalist Icon
1_singleton_analogy
1_singleton_analogy
1_singleton_uml
1_singleton_uml
1_singleton_minimalist
1_singleton_minimalist

7. 2. Factory Method

Complexity: ★★☆☆☆ | Frequency: ★★★★★

7.1. Definition

Factory Method defines an interface for creating objects but lets subclasses decide which class to instantiate.

7.2. Why Is It Required?

When your code needs to work with various types of objects but shouldn't know the exact classes in advance. It decouples the client code from the concrete classes it needs to instantiate.

7.3. With vs Without

Without Factory MethodWith Factory Method
if-else chains everywhere for object creationClean, extensible creation logic
Adding new types requires modifying existing codeAdd new types by adding new classes

7.4. Best Use Cases

  • Web Dev: Payment gateway selection, UI component creation
  • Game Dev: Spawning different enemy types, platform-specific renderers
  • Enterprise: Document generators (PDF, Word, Excel), notification services

7.5. Analogy

A Logistics Company — A logistics company can deliver packages, but the actual transport (truck, ship, plane) depends on the destination. The company doesn't care how; it just calls "deliver()" and the appropriate transport handles it.

7.6. Code Examples

Python:

from abc import ABC, abstractmethod

class Notification(ABC):
    @abstractmethod
    def send(self, message: str) -> None:
        pass

class EmailNotification(Notification):
    def send(self, message: str) -> None:
        print(f"📧 Sending Email: {message}")

class SMSNotification(Notification):
    def send(self, message: str) -> None:
        print(f"📱 Sending SMS: {message}")

class NotificationFactory(ABC):
    @abstractmethod
    def create_notification(self) -> Notification:
        pass
    
    def notify(self, message: str) -> None:
        notification = self.create_notification()
        notification.send(message)

class EmailFactory(NotificationFactory):
    def create_notification(self) -> Notification:
        return EmailNotification()

# Usage
EmailFactory().notify("Your order has shipped!")

C++:

class Notification {
public:
    virtual void send(const std::string& message) = 0;
};

class EmailNotification : public Notification {
public:
    void send(const std::string& message) override {
        std::cout << "📧 Email: " << message << std::endl;
    }
};

class NotificationFactory {
public:
    virtual std::unique_ptr<Notification> createNotification() = 0;
    
    void notify(const std::string& message) {
        auto notification = createNotification();
        notification->send(message);
    }
};

class EmailFactory : public NotificationFactory {
public:
    std::unique_ptr<Notification> createNotification() override {
        return std::make_unique<EmailNotification>();
    }
};

JavaScript:

class Notification {
    send(message) { throw new Error("Must implement"); }
}

class EmailNotification extends Notification {
    send(message) { console.log(`📧 Email: ${message}`); }
}

class SMSNotification extends Notification {
    send(message) { console.log(`📱 SMS: ${message}`); }
}

class NotificationFactory {
    createNotification() { throw new Error("Must implement"); }
    
    notify(message) {
        const notification = this.createNotification();
        notification.send(message);
    }
}

class EmailFactory extends NotificationFactory {
    createNotification() { return new EmailNotification(); }
}

new EmailFactory().notify("Your order shipped!");
AnalogyUML DiagramMinimalist Icon
2_factory_method_analogy
2_factory_method_analogy
2_factory_method_uml
2_factory_method_uml
2_factory_method_minimalist
2_factory_method_minimalist

8. 3. Abstract Factory

Complexity: ★★★☆☆ | Frequency: ★★★★☆

8.1. Definition

Abstract Factory provides an interface for creating families of related objects without specifying concrete classes. It's a factory of factories.

8.2. Why Is It Required?

When your system needs to work with multiple families of related products, ensuring products from one family aren't mixed with another.

8.3. With vs Without

Without Abstract FactoryWith Abstract Factory
Risk of mixing incompatible componentsGuaranteed family consistency
N × M classes for N abstractions × M implementationsN + M classes total

8.4. Best Use Cases

  • Web Dev: UI theme systems (Light/Dark mode), cross-platform UI
  • Game Dev: Platform-specific game assets (PC, Console, Mobile)
  • Enterprise: Multi-database support, multi-cloud configurations

8.5. Analogy

IKEA Furniture Collections — IKEA sells complete furniture collections (Modern, Victorian, Minimalist). Each collection has chairs, tables, sofas that match perfectly. You pick a collection, and everything from it looks good together.

8.6. Code Examples

Python:

from abc import ABC, abstractmethod

class Button(ABC):
    @abstractmethod
    def render(self) -> str:
        pass

class Checkbox(ABC):
    @abstractmethod
    def render(self) -> str:
        pass

class LightButton(Button):
    def render(self) -> str:
        return "[ Light Button ]"

class DarkButton(Button):
    def render(self) -> str:
        return "[ Dark Button ]"

class UIFactory(ABC):
    @abstractmethod
    def create_button(self) -> Button:
        pass
    
    @abstractmethod
    def create_checkbox(self) -> Checkbox:
        pass

class LightThemeFactory(UIFactory):
    def create_button(self) -> Button:
        return LightButton()
    
    def create_checkbox(self) -> Checkbox:
        return LightCheckbox()

class DarkThemeFactory(UIFactory):
    def create_button(self) -> Button:
        return DarkButton()
    
    def create_checkbox(self) -> Checkbox:
        return DarkCheckbox()

# Usage
def render_ui(factory: UIFactory):
    button = factory.create_button()
    print(button.render())

render_ui(DarkThemeFactory())

C++:

class UIFactory {
public:
    virtual std::unique_ptr<Button> createButton() = 0;
    virtual std::unique_ptr<Checkbox> createCheckbox() = 0;
};

class LightThemeFactory : public UIFactory {
public:
    std::unique_ptr<Button> createButton() override {
        return std::make_unique<LightButton>();
    }
    std::unique_ptr<Checkbox> createCheckbox() override {
        return std::make_unique<LightCheckbox>();
    }
};

JavaScript:

class UIFactory {
    createButton() { throw new Error("Must implement"); }
    createCheckbox() { throw new Error("Must implement"); }
}

class LightThemeFactory extends UIFactory {
    createButton() { return new LightButton(); }
    createCheckbox() { return new LightCheckbox(); }
}

class DarkThemeFactory extends UIFactory {
    createButton() { return new DarkButton(); }
    createCheckbox() { return new DarkCheckbox(); }
}
AnalogyUML DiagramMinimalist Icon
3_abstract_factory_analogy
3_abstract_factory_analogy
3_abstract_factory_uml
3_abstract_factory_uml
3_abstract_factory_minimalist
3_abstract_factory_minimalist

9. 4. Builder

Complexity: ★★★☆☆ | Frequency: ★★★★☆

9.1. Definition

Builder separates the construction of a complex object from its representation, allowing the same construction process to create different representations.

9.2. Why Is It Required?

When creating an object requires many steps, optional parameters, or multiple configurations. Avoids "telescoping constructors" with many parameters.

9.3. With vs Without

Without BuilderWith Builder
new Pizza(true, false, true, 2, "large") — crypticPizza.builder().cheese().pepperoni().large().build() — clear
Many constructor overloads for optional paramsHandle any combination cleanly

9.4. Best Use Cases

  • Web Dev: Building HTTP requests, SQL query builders, form builders
  • Game Dev: Character creation systems, level builders
  • Enterprise: Report generators, configuration objects

9.5. Analogy

Ordering a Custom Burger — At a build-your-own burger restaurant, you say: "beef patty, add cheese, add bacon, no onions, extra pickles." The chef follows instructions step by step and gives you exactly what you asked for.

9.6. Code Examples

Python:

class HttpRequest:
    def __init__(self):
        self.method = "GET"
        self.url = ""
        self.headers = {}
        self.body = None

class HttpRequestBuilder:
    def __init__(self):
        self._request = HttpRequest()
    
    def method(self, method: str) -> 'HttpRequestBuilder':
        self._request.method = method
        return self
    
    def url(self, url: str) -> 'HttpRequestBuilder':
        self._request.url = url
        return self
    
    def header(self, key: str, value: str) -> 'HttpRequestBuilder':
        self._request.headers[key] = value
        return self
    
    def json_body(self, data: dict) -> 'HttpRequestBuilder':
        import json
        self._request.body = json.dumps(data)
        self.header("Content-Type", "application/json")
        return self
    
    def build(self) -> HttpRequest:
        return self._request

# Usage
request = (HttpRequestBuilder()
    .method("POST")
    .url("https://api.example.com/users")
    .header("Authorization", "Bearer token")
    .json_body({"name": "Sankar"})
    .build())

C++:

class HttpRequestBuilder {
private:
    HttpRequest request;

public:
    HttpRequestBuilder& setMethod(const std::string& method) {
        request.method = method;
        return *this;
    }
    
    HttpRequestBuilder& setUrl(const std::string& url) {
        request.url = url;
        return *this;
    }
    
    HttpRequestBuilder& addHeader(const std::string& key, const std::string& value) {
        request.headers[key] = value;
        return *this;
    }
    
    HttpRequest build() {
        return request;
    }
};

JavaScript:

class HttpRequestBuilder {
    constructor() {
        this.request = { method: "GET", url: "", headers: {}, body: null };
    }
    
    method(method) { this.request.method = method; return this; }
    url(url) { this.request.url = url; return this; }
    header(key, value) { this.request.headers[key] = value; return this; }
    jsonBody(data) {
        this.request.body = JSON.stringify(data);
        return this.header("Content-Type", "application/json");
    }
    build() { return this.request; }
}

const request = new HttpRequestBuilder()
    .method("POST")
    .url("https://api.example.com")
    .jsonBody({ name: "Sankar" })
    .build();
AnalogyUML DiagramMinimalist Icon
4_builder_analogy
4_builder_analogy
4_builder_uml
4_builder_uml
4_builder_minimalist
4_builder_minimalist

10. 5. Prototype

Complexity: ★★☆☆☆ | Frequency: ★★★☆☆

10.1. Definition

Prototype creates new objects by cloning existing ones, rather than creating from scratch.

10.2. Why Is It Required?

When object creation is expensive (database calls, complex initialization), or when you need objects with slight variations from a template.

10.3. With vs Without

Without PrototypeWith Prototype
Recreate complex objects from scratchClone and modify — much faster
Expensive initialization repeatedClone cached templates

10.4. Best Use Cases

  • Web Dev: Cloning form templates, cached API responses
  • Game Dev: Spawning enemies from templates, particle systems
  • Enterprise: Document templates, default settings

10.5. Analogy

Cell Division (Mitosis) — Cells don't build new cells from scratch. They duplicate themselves — DNA, organelles, everything copied, then split. The new cell is a perfect clone, ready to differentiate.

10.6. Code Examples

Python:

import copy

class GameCharacter:
    def __init__(self, name, health, attack, skills):
        self.name = name
        self.health = health
        self.attack = attack
        self.skills = skills
    
    def clone(self):
        return copy.deepcopy(self)

# Prototype registry
prototypes = {
    "warrior": GameCharacter("Warrior", 100, 15, ["Slash", "Block"]),
    "mage": GameCharacter("Mage", 60, 25, ["Fireball", "Teleport"])
}

# Clone and customize
warrior1 = prototypes["warrior"].clone()
warrior1.name = "Conan"

warrior2 = prototypes["warrior"].clone()
warrior2.name = "Xena"
warrior2.skills.append("Leap Attack")

C++:

class GameCharacter {
public:
    std::string name;
    int health, attack;
    std::vector<std::string> skills;
    
    virtual std::unique_ptr<GameCharacter> clone() const = 0;
};

class Warrior : public GameCharacter {
public:
    std::unique_ptr<GameCharacter> clone() const override {
        return std::make_unique<Warrior>(*this);
    }
};

JavaScript:

class GameCharacter {
    constructor(name, health, attack, skills) {
        this.name = name;
        this.health = health;
        this.attack = attack;
        this.skills = skills;
    }
    
    clone() {
        return new GameCharacter(
            this.name, 
            this.health, 
            this.attack, 
            [...this.skills]  // Deep copy array
        );
    }
}

const warrior = new GameCharacter("Warrior", 100, 15, ["Slash"]);
const warrior2 = warrior.clone();
warrior2.name = "Custom Warrior";
AnalogyUML DiagramMinimalist Icon
5_prototype_analogy
5_prototype_analogy
5_prototype_uml
5_prototype_uml
5_prototype_minimalist
5_prototype_minimalist

Structural Patterns

Mnemonic: "Adapters Bridge Composite Decorators, Facades Fly with Proxies"

Adapter, Bridge, Composite, Decorator, Facade, Flyweight, Proxy

Structural patterns deal with object composition — how classes and objects combine to form larger structures.


11. 6. Adapter

Complexity: ★★☆☆☆ | Frequency: ★★★★★

11.1. Definition

Adapter converts the interface of a class into another interface that clients expect.

11.2. Analogy

Power Plug Adapters — Your laptop charger doesn't fit foreign outlets. A plug adapter converts between socket types without rewiring anything.

11.3. Best Use Cases

  • Web Dev: Integrating multiple payment gateways, normalizing API formats
  • Game Dev: Supporting multiple input devices
  • Enterprise: Legacy system integration

11.4. Code Example (Python)

class LegacyXmlService:
    def parse_xml_document(self, xml_string):
        return ET.fromstring(xml_string)

class XmlParserAdapter:  # Adapts to expected interface
    def __init__(self):
        self._legacy = LegacyXmlService()
    
    def parse(self, data: str) -> dict:
        root = self._legacy.parse_xml_document(data)
        return self._element_to_dict(root)
AnalogyUML DiagramMinimalist Icon
6_adapter_analogy
6_adapter_analogy
6_adapter_uml
6_adapter_uml
6_adapter_minimalist
6_adapter_minimalist

12. 7. Bridge

Complexity: ★★★★☆ | Frequency: ★★★☆☆

12.1. Definition

Bridge decouples an abstraction from its implementation so both can vary independently.

12.2. Analogy

Remote Control and TV — A remote (abstraction) works with any TV (implementation). Upgrade your TV without changing how you use the remote.

12.3. Best Use Cases

  • Web Dev: UI components rendering on different platforms
  • Game Dev: Game objects across different rendering engines
  • Enterprise: Notification system (message types × delivery channels)

12.4. Code Example (Python)

class MessageSender(ABC):  # Implementation
    @abstractmethod
    def send(self, title: str, body: str) -> None:
        pass

class EmailSender(MessageSender):
    def send(self, title, body):
        print(f"📧 EMAIL | {title}: {body}")

class Notification(ABC):  # Abstraction
    def __init__(self, sender: MessageSender):
        self._sender = sender

class AlertNotification(Notification):
    def notify(self, message):
        self._sender.send("🚨 ALERT", message)

# Mix and match!
AlertNotification(EmailSender()).notify("Server down!")
AlertNotification(SmsSender()).notify("Server down!")
AnalogyUML DiagramMinimalist Icon
7_bridge_analogy
7_bridge_analogy
7_bridge_uml
7_bridge_uml
7_bridge_minimalist
7_bridge_minimalist

13. 8. Composite

Complexity: ★★★☆☆ | Frequency: ★★★★☆

13.1. Definition

Composite lets you compose objects into tree structures to represent part-whole hierarchies, treating individuals and groups uniformly.

13.2. Analogy

Company Organization Chart — A CEO can ask for total salary of "Engineering Department." That department contains teams, each with people. Same getTotalSalary() works on individual, team, or entire department.

13.3. Best Use Cases

  • Web Dev: DOM trees, nested menus, form builders
  • Game Dev: Scene graphs, inventory systems
  • Enterprise: Organizational hierarchies, bill-of-materials

13.4. Code Example (Python)

class FileSystemItem(ABC):
    @abstractmethod
    def get_size(self) -> int:
        pass

class File(FileSystemItem):
    def __init__(self, name, size):
        self.name = name
        self._size = size
    
    def get_size(self):
        return self._size

class Folder(FileSystemItem):
    def __init__(self, name):
        self.name = name
        self._children = []
    
    def add(self, item):
        self._children.append(item)
    
    def get_size(self):
        return sum(child.get_size() for child in self._children)
AnalogyUML DiagramMinimalist Icon
8_composite_analogy
8_composite_analogy
8_composite_uml
8_composite_uml
8_composite_minimalist
8_composite_minimalist

14. 9. Decorator

Complexity: ★★★☆☆ | Frequency: ★★★★★

14.1. Definition

Decorator attaches additional responsibilities to an object dynamically, providing flexible alternative to subclassing.

14.2. Analogy

Coffee Orders at a Café — Start with espresso. Add milk (latte), add syrup, add whipped cream. Each addition "decorates" the base, adding cost and features.

14.3. Best Use Cases

  • Web Dev: Middleware stacks, adding caching/logging/auth
  • Game Dev: Power-ups, weapon modifications, buff/debuff systems
  • Enterprise: Stream wrappers (encryption, compression)

14.4. Code Example (Python)

class DataSource(ABC):
    @abstractmethod
    def write(self, data: str) -> None:
        pass

class FileDataSource(DataSource):
    def write(self, data):
        print(f"Writing: {data}")

class DataSourceDecorator(DataSource):
    def __init__(self, source: DataSource):
        self._wrapped = source

class EncryptionDecorator(DataSourceDecorator):
    def write(self, data):
        encrypted = self._encrypt(data)
        self._wrapped.write(encrypted)

# Stack decorators!
decorated = LoggingDecorator(
    CompressionDecorator(
        EncryptionDecorator(FileDataSource())
    )
)
AnalogyUML DiagramMinimalist Icon
9_decorator_analogy
9_decorator_analogy
9_decorator_uml
9_decorator_uml
9_decorator_minimalist
9_decorator_minimalist

15. 10. Facade

Complexity: ★★☆☆☆ | Frequency: ★★★★★

15.1. Definition

Facade provides a simplified interface to a complex subsystem.

15.2. Analogy

Hotel Concierge — A hotel has restaurants, spa, laundry, transportation. As a guest, you just talk to the concierge for everything. One call handles all your needs.

15.3. Best Use Cases

  • Web Dev: SDK libraries, jQuery (DOM simplification)
  • Game Dev: High-level engine APIs
  • Enterprise: Service aggregation layers, API gateways

15.4. Code Example (Python)

class VideoConversionFacade:
    def convert(self, filename: str, format: str) -> str:
        """Simple interface hiding complex subsystems"""
        file = VideoFile(filename)
        codec = CodecFactory.extract(file)
        buffer = BitrateReader.read(filename, codec)
        buffer = AudioMixer.fix(buffer)
        return VideoConverter.convert(buffer, format)

# Usage - So simple!
VideoConversionFacade().convert("movie.avi", "mp4")
AnalogyUML DiagramMinimalist Icon
10_facade_analogy
10_facade_analogy
10_facade_uml
10_facade_uml
10_facade_minimalist
10_facade_minimalist

16. 11. Flyweight

Complexity: ★★★★☆ | Frequency: ★★☆☆☆

16.1. Definition

Flyweight minimizes memory by sharing common state among multiple objects.

16.2. Analogy

Typography and Fonts — A 100,000-character document doesn't store separate images for each letter. It stores which font to use (shared) and where each letter appears (unique).

16.3. Best Use Cases

  • Web Dev: Character rendering in text editors, icon libraries
  • Game Dev: Particle systems, terrain tiles, crowds
  • Enterprise: String interning, connection pools

16.4. Code Example (Python)

class TreeFactory:
    _tree_types = {}
    
    @classmethod
    def get_tree_type(cls, name, color, texture):
        key = f"{name}_{color}"
        if key not in cls._tree_types:
            cls._tree_types[key] = TreeType(name, color, texture)
        return cls._tree_types[key]

# 1000 trees share only 3 TreeType objects!
for i in range(1000):
    tree_type = TreeFactory.get_tree_type("Oak", "green", "oak.png")
    trees.append(Tree(x, y, tree_type))
AnalogyUML DiagramMinimalist Icon
11_flyweight_analogy
11_flyweight_analogy
11_flyweight_uml
11_flyweight_uml
11_flyweight_minimalist
11_flyweight_minimalist

17. 12. Proxy

Complexity: ★★★☆☆ | Frequency: ★★★★☆

17.1. Definition

Proxy provides a surrogate or placeholder for another object to control access.

17.2. Analogy

Credit Card as Proxy for Cash — A credit card is a proxy for money in your bank. Same "payment interface" but adds controls: spending limits, delayed withdrawal, transaction logging.

17.3. Best Use Cases

  • Web Dev: Image lazy loading, API caching, authentication wrappers
  • Game Dev: Asset loading proxies, network game objects
  • Enterprise: Service proxies, access control wrappers

17.4. Code Example (Python)

class ImageProxy:
    def __init__(self, filename):
        self.filename = filename
        self._real_image = None  # Lazy
    
    def display(self):
        if self._real_image is None:
            self._real_image = HighResolutionImage(self.filename)
        self._real_image.display()
    
    def get_info(self):
        # Can answer without loading heavy image
        return f"Proxy for {self.filename}"
AnalogyUML DiagramMinimalist Icon
12_proxy_analogy
12_proxy_analogy
12_proxy_uml
12_proxy_uml
12_proxy_minimalist
12_proxy_minimalist

Behavioral Patterns

Mnemonic: "Clever Code Inside Iterators Makes Memories Of State Strategies, Templates, & Visitors"

Chain of Responsibility, Command, Interpreter, Iterator, Mediator, Memento, Observer, State, Strategy, Template Method, Visitor

Behavioral patterns deal with communication between objects.


18. 13. Chain of Responsibility

Complexity: ★★★☆☆ | Frequency: ★★★☆☆

18.1. Definition

Chain of Responsibility passes requests along a chain of handlers. Each handler decides to process or pass it along.

18.2. Analogy

Customer Support Escalation — Level 1 handles basic issues. Can't help? Escalate to Level 2. Still unresolved? Level 3 specialist. Each level either handles or passes up.

18.3. Best Use Cases

  • Web Dev: Middleware pipelines (Express, Django), authentication chains
  • Game Dev: Input event handling, damage calculations
  • Enterprise: Approval workflows, exception handling

18.4. Code Example (Python)

class SupportHandler(ABC):
    def __init__(self):
        self._next = None
    
    def set_next(self, handler):
        self._next = handler
        return handler
    
    @abstractmethod
    def handle(self, request):
        pass
    
    def pass_to_next(self, request):
        if self._next:
            return self._next.handle(request)
        return "No handler available"

class Level1Support(SupportHandler):
    def handle(self, request):
        if request.amount <= 100:
            return "Level 1: Approved"
        return self.pass_to_next(request)

# Build chain
level1.set_next(level2).set_next(manager)
level1.handle(request)
AnalogyUML DiagramMinimalist Icon
13_chain_of_responsibility_analogy
13_chain_of_responsibility_analogy
13_chain_of_responsibility_uml
13_chain_of_responsibility_uml
13_chain_of_responsibility_minimalist
13_chain_of_responsibility_minimalist

19. 14. Command

Complexity: ★★★☆☆ | Frequency: ★★★★★

19.1. Definition

Command encapsulates a request as an object, enabling parameterization, queuing, logging, and undoable operations.

19.2. Analogy

Restaurant Order System — Waiter writes order on ticket (command). Ticket goes to kitchen. Waiter doesn't cook; kitchen doesn't take orders. Tickets can be queued or cancelled.

19.3. Best Use Cases

  • Web Dev: Undo/redo in editors, keyboard shortcuts, request queues
  • Game Dev: Input replay, macro recording, turn-based actions
  • Enterprise: Transaction systems, audit logging, job queues

19.4. Code Example (Python)

class Command(ABC):
    @abstractmethod
    def execute(self) -> None:
        pass
    
    @abstractmethod
    def undo(self) -> None:
        pass

class WriteCommand(Command):
    def __init__(self, editor, text):
        self.editor = editor
        self.text = text
    
    def execute(self):
        self.editor.write(self.text)
    
    def undo(self):
        self.editor.delete(len(self.text))

class CommandManager:
    def __init__(self):
        self.history = []
    
    def execute(self, command):
        command.execute()
        self.history.append(command)
    
    def undo(self):
        if self.history:
            self.history.pop().undo()
AnalogyUML DiagramMinimalist Icon
14_command_analogy
14_command_analogy
14_command_uml
14_command_uml
14_command_minimalist
14_command_minimalist

20. 15. Interpreter

Complexity: ★★★★★ | Frequency: ★☆☆☆☆

20.1. Definition

Interpreter defines a grammatical representation for a language and provides an interpreter to evaluate sentences.

20.2. Analogy

Musical Notation — Sheet music is a language. Each symbol interpreted by musician: whole note = 4 beats, half note = 2 beats. Musician interprets according to music grammar.

20.3. Best Use Cases

  • Web Dev: Template engines, query parsers
  • Game Dev: Scripting languages, formula evaluators
  • Enterprise: Business rule engines, SQL-like parsers

20.4. Code Example (Python)

class Expression(ABC):
    @abstractmethod
    def interpret(self, context: dict) -> int:
        pass

class Number(Expression):
    def __init__(self, value):
        self.value = value
    def interpret(self, context):
        return self.value

class Variable(Expression):
    def __init__(self, name):
        self.name = name
    def interpret(self, context):
        return context[self.name]

class Add(Expression):
    def __init__(self, left, right):
        self.left = left
        self.right = right
    def interpret(self, context):
        return self.left.interpret(context) + self.right.interpret(context)

# (x + 5)
expr = Add(Variable("x"), Number(5))
print(expr.interpret({"x": 10}))  # 15
AnalogyUML DiagramMinimalist Icon
15_interpreter_analogy
15_interpreter_analogy
15_interpreter_uml
15_interpreter_uml
15_interpreter_minimalist
15_interpreter_minimalist

21. 16. Iterator

Complexity: ★★☆☆☆ | Frequency: ★★★★★

21.1. Definition

Iterator provides way to access elements of a collection sequentially without exposing underlying representation.

21.2. Analogy

Spotify Playlist — When you hit "next track," you don't care if playlist is array, linked list, or from server. Just have "next," "previous," "current."

21.3. Best Use Cases

  • Web Dev: Paginated data, lazy loading streams
  • Game Dev: Entity iteration, pathfinding results
  • Enterprise: Database cursors, message queue consumers

21.4. Code Example (Python)

class DepthFirstIterator:
    def __init__(self, root):
        self._stack = [root] if root else []
    
    def has_next(self):
        return len(self._stack) > 0
    
    def next(self):
        current = self._stack.pop()
        for child in reversed(current.children):
            self._stack.append(child)
        return current

# Usage
dfs = DepthFirstIterator(tree.root)
while dfs.has_next():
    print(dfs.next().value)
AnalogyUML DiagramMinimalist Icon
16_iterator_analogy
16_iterator_analogy
16_iterator_uml
16_iterator_uml
16_iterator_minimalist
16_iterator_minimalist

22. 17. Mediator

Complexity: ★★★☆☆ | Frequency: ★★★☆☆

22.1. Definition

Mediator defines object that encapsulates how objects interact, promoting loose coupling.

22.2. Analogy

Air Traffic Control Tower — Planes don't communicate directly. They talk to control tower (mediator), which coordinates everything. Tower knows all planes; planes only know tower.

22.3. Best Use Cases

  • Web Dev: Form validation coordination, chat rooms
  • Game Dev: NPC coordination, traffic systems
  • Enterprise: Service orchestration, event aggregators

22.4. Code Example (Python)

class ChatRoom:
    def __init__(self):
        self.users = []
    
    def add_user(self, user):
        self.users.append(user)
    
    def send_message(self, message, sender):
        for user in self.users:
            if user != sender:
                user.receive(message, sender)

class User:
    def __init__(self, name, room):
        self.name = name
        self.room = room
        room.add_user(self)
    
    def send(self, message):
        self.room.send_message(message, self)
    
    def receive(self, message, sender):
        print(f"{self.name} received from {sender.name}: {message}")
AnalogyUML DiagramMinimalist Icon
17_mediator_analogy
17_mediator_analogy
17_mediator_uml
17_mediator_uml
17_mediator_minimalist
17_mediator_minimalist

23. 18. Memento

Complexity: ★★★☆☆ | Frequency: ★★★☆☆

23.1. Definition

Memento captures and externalizes object's internal state without violating encapsulation, for later restoration.

23.2. Analogy

Video Game Save Points — Save at checkpoints captures everything: health, inventory, position. Die? Load from save and restore exactly that state.

23.3. Best Use Cases

  • Web Dev: Form drafts, state management snapshots
  • Game Dev: Save systems, undo in level editors
  • Enterprise: Transaction rollback, document versioning

23.4. Code Example (Python)

class EditorMemento:
    def __init__(self, content, cursor):
        self._content = content
        self._cursor = cursor
    
    def get_state(self):
        return {"content": self._content, "cursor": self._cursor}

class TextEditor:
    def __init__(self):
        self.content = ""
        self.cursor = 0
    
    def save(self):
        return EditorMemento(self.content, self.cursor)
    
    def restore(self, memento):
        state = memento.get_state()
        self.content = state["content"]
        self.cursor = state["cursor"]

class History:
    def __init__(self, editor):
        self.editor = editor
        self.history = []
    
    def backup(self):
        self.history.append(self.editor.save())
    
    def undo(self):
        if self.history:
            self.editor.restore(self.history.pop())
AnalogyUML DiagramMinimalist Icon
18_memento_analogy
18_memento_analogy
18_memento_uml
18_memento_uml
18_memento_minimalist
18_memento_minimalist

24. 19. Observer

Complexity: ★★☆☆☆ | Frequency: ★★★★★

24.1. Definition

Observer defines one-to-many dependency so when one object changes, all dependents are notified automatically.

24.2. Analogy

YouTube Subscriptions — Subscribe to channel, receive notifications on new videos. You don't poll; YouTube pushes to all subscribers. Unsubscribe anytime.

24.3. Best Use Cases

  • Web Dev: Event listeners, reactive state (Redux, MobX), WebSocket
  • Game Dev: Achievement systems, UI updates
  • Enterprise: Stock tickers, notification systems

24.4. Code Example (Python)

class Stock:
    def __init__(self, symbol, price):
        self.symbol = symbol
        self._price = price
        self._observers = set()
    
    @property
    def price(self):
        return self._price
    
    @price.setter
    def price(self, value):
        self._price = value
        self._notify()
    
    def attach(self, observer):
        self._observers.add(observer)
    
    def _notify(self):
        for obs in self._observers:
            obs.update(self)

class PriceDisplay:
    def update(self, stock):
        print(f"Display: {stock.symbol} = ${stock.price}")

# Usage
aapl = Stock("AAPL", 150)
aapl.attach(PriceDisplay())
aapl.price = 155  # Display automatically notified
AnalogyUML DiagramMinimalist Icon
19_observer_analogy
19_observer_analogy
19_observer_uml
19_observer_uml
19_observer_minimalist
19_observer_minimalist

25. 20. State

Complexity: ★★★☆☆ | Frequency: ★★★★☆

25.1. Definition

State allows object to alter behavior when internal state changes. Object appears to change its class.

25.2. Analogy

Vending Machine — Behaves differently per state: Idle (waiting for money), HasMoney (waiting for selection), Dispensing (giving product). Same buttons, different results per state.

25.3. Best Use Cases

  • Web Dev: Form wizards, checkout flows
  • Game Dev: Character states (idle, running, jumping), enemy AI
  • Enterprise: Order processing, approval workflows

25.4. Code Example (Python)

class OrderState(ABC):
    @abstractmethod
    def proceed(self, order):
        pass

class PendingState(OrderState):
    def proceed(self, order):
        print("Payment confirmed!")
        order.set_state(ProcessingState())

class ProcessingState(OrderState):
    def proceed(self, order):
        print("Order shipped!")
        order.set_state(ShippedState())

class Order:
    def __init__(self):
        self._state = PendingState()
    
    def set_state(self, state):
        self._state = state
    
    def proceed(self):
        self._state.proceed(self)

order = Order()
order.proceed()  # Payment confirmed!
order.proceed()  # Order shipped!
AnalogyUML DiagramMinimalist Icon
20_state_analogy
20_state_analogy
20_state_uml
20_state_uml
20_state_minimalist
20_state_minimalist

26. 21. Strategy

Complexity: ★★☆☆☆ | Frequency: ★★★★★

26.1. Definition

Strategy defines family of algorithms, encapsulates each, makes them interchangeable.

26.2. Analogy

Google Maps Route Options — Choose: Fastest, Shortest, Avoid Highways, Scenic. Same destination, different strategies. Switch anytime.

26.3. Best Use Cases

  • Web Dev: Payment methods, sorting options, auth strategies
  • Game Dev: AI behaviors, movement patterns
  • Enterprise: Tax calculation, shipping algorithms, discount rules

26.4. Code Example (Python)

class ShippingStrategy(ABC):
    @abstractmethod
    def calculate(self, items) -> float:
        pass

class StandardShipping(ShippingStrategy):
    def calculate(self, items):
        return 5.0 + sum(i.weight for i in items) * 0.5

class ExpressShipping(ShippingStrategy):
    def calculate(self, items):
        return 15.0 + sum(i.weight for i in items) * 1.0

class ShoppingCart:
    def __init__(self):
        self.items = []
        self._shipping = StandardShipping()
    
    def set_shipping(self, strategy):
        self._shipping = strategy
    
    def get_total(self):
        return sum(i.price for i in self.items) + self._shipping.calculate(self.items)

cart = ShoppingCart()
cart.set_shipping(ExpressShipping())  # Easy swap!
AnalogyUML DiagramMinimalist Icon
21_strategy_analogy
21_strategy_analogy
21_strategy_uml
21_strategy_uml
21_strategy_minimalist
21_strategy_minimalist

27. 22. Template Method

Complexity: ★★☆☆☆ | Frequency: ★★★★☆

27.1. Definition

Template Method defines skeleton of algorithm in base class, letting subclasses override specific steps.

27.2. Analogy

Building a House — Every house: Foundation → Frame → Roof → Exterior → Interior. Sequence fixed; materials/styles differ (brick vs wood, flat vs gable).

27.3. Best Use Cases

  • Web Dev: HTTP lifecycle (parse → auth → handle → respond)
  • Game Dev: Game loop (input → update → render)
  • Enterprise: Report generation, ETL pipelines

27.4. Code Example (Python)

class DataMiner(ABC):
    def mine(self, path):
        """Template method - skeleton"""
        raw = self.extract(path)
        parsed = self.parse(raw)
        analyzed = self.analyze(parsed)
        self.generate_report(analyzed)
    
    @abstractmethod
    def extract(self, path):
        pass
    
    @abstractmethod
    def parse(self, data):
        pass
    
    def analyze(self, data):  # Default implementation
        return {"count": len(data)}
    
    def generate_report(self, analysis):  # Hook
        print(f"Report: {analysis}")

class PDFMiner(DataMiner):
    def extract(self, path):
        return f"PDF content from {path}"
    
    def parse(self, data):
        return data.split()

class CSVMiner(DataMiner):
    def extract(self, path):
        return "a,b,c\n1,2,3"
    
    def parse(self, data):
        return [line.split(",") for line in data.split("\n")]
AnalogyUML DiagramMinimalist Icon
22_template_method_analogy
22_template_method_analogy
22_template_method_uml
22_template_method_uml
22_template_method_minimalist
22_template_method_minimalist

28. 23. Visitor

Complexity: ★★★★★ | Frequency: ★★☆☆☆

28.1. Definition

Visitor lets you define new operations on objects without changing their classes.

28.2. Analogy

Tax Auditor Visiting Departments — Auditor visits Finance, HR, Engineering. Each department has own structure, but auditor applies same audit process. Add new auditors without changing departments.

28.3. Best Use Cases

  • Web Dev: AST transformations (Babel, ESLint), DOM traversal
  • Game Dev: Collision handling, serialization
  • Enterprise: Document exporters (PDF, HTML, XML)

28.4. Code Example (Python)

class DocumentVisitor(ABC):
    @abstractmethod
    def visit_heading(self, heading):
        pass
    @abstractmethod
    def visit_paragraph(self, paragraph):
        pass

class HTMLExporter(DocumentVisitor):
    def visit_heading(self, h):
        return f"<h{h.level}>{h.text}</h{h.level}>"
    def visit_paragraph(self, p):
        return f"<p>{p.text}</p>"

class MarkdownExporter(DocumentVisitor):
    def visit_heading(self, h):
        return f"{'#' * h.level} {h.text}"
    def visit_paragraph(self, p):
        return p.text

class Heading:
    def __init__(self, level, text):
        self.level = level
        self.text = text
    def accept(self, visitor):
        return visitor.visit_heading(self)

# Export same document to different formats
doc.export(HTMLExporter())
doc.export(MarkdownExporter())

28.5.

AnalogyUML DiagramMinimalist Icon
23_visitor_analogy
23_visitor_analogy
23_visitor_uml
23_visitor_uml
23_visitor_minimalist
23_visitor_minimalist

Additional Modern Patterns

29. 24. Dependency Injection

Complexity: ★★★☆☆ | Frequency: ★★★★★

Objects receive dependencies from external sources rather than creating them internally. Enables loose coupling and easy testing.

# Without DI - tightly coupled
class UserService:
    def __init__(self):
        self.db = MySQLDatabase()  # Hard-coded

# With DI - loosely coupled
class UserService:
    def __init__(self, db: Database):  # Injected
        self.db = db

service = UserService(PostgresDatabase())  # Easy swap

30. 25. Repository

Complexity: ★★★☆☆ | Frequency: ★★★★★

Mediates between domain and data mapping layers, acting like in-memory collection.

class UserRepository:
    def __init__(self, db_session):
        self.session = db_session
    
    def find_by_id(self, user_id):
        return self.session.query(User).filter_by(id=user_id).first()
    
    def save(self, user):
        self.session.add(user)
        self.session.commit()

31. 26. Unit of Work

Complexity: ★★★★☆ | Frequency: ★★★★☆

Maintains list of objects affected by business transaction and coordinates writing changes.

class UnitOfWork:
    def __init__(self):
        self.new = []
        self.dirty = []
        self.removed = []
    
    def commit(self):
        # Insert new, update dirty, delete removed
        # All in single transaction
        pass

Quick Reference & Mnemonics

32. Pattern Categories

CategoryPatternsFocus
CreationalSingleton, Factory Method, Abstract Factory, Builder, PrototypeObject creation
StructuralAdapter, Bridge, Composite, Decorator, Facade, Flyweight, ProxyObject composition
BehavioralChain of Responsibility, Command, Interpreter, Iterator, Mediator, Memento, Observer, State, Strategy, Template Method, VisitorObject communication

33. When to Use What

ProblemPattern
Need exactly one instanceSingleton
Creating objects without specifying exact classFactory Method
Creating families of related objectsAbstract Factory
Complex object constructionBuilder
Cloning existing objectsPrototype
Making incompatible interfaces work togetherAdapter
Separating abstraction from implementationBridge
Tree structures with uniform treatmentComposite
Adding behavior dynamicallyDecorator
Simplifying complex subsystemsFacade
Sharing state to save memoryFlyweight
Controlling access to objectsProxy
Passing requests along a chainChain of Responsibility
Encapsulating requests as objectsCommand
Building language interpretersInterpreter
Sequential access to collectionsIterator
Centralizing complex communicationMediator
Capturing and restoring stateMemento
Notifying dependents of changesObserver
Changing behavior based on stateState
Swapping algorithms at runtimeStrategy
Defining algorithm skeletonsTemplate Method
Adding operations without changing classesVisitor

34. Memory Mnemonics

34.1. Creational (5 patterns)

"Smart Factories Always Build Prototypes"

Singleton, Factory Method, Abstract Factory, Builder, Prototype

34.2. Structural (7 patterns)

"Adapters Bridge Composite Decorators, Facades Fly with Proxies"

Adapter, Bridge, Composite, Decorator, Facade, Flyweight, Proxy

34.3. Behavioral (11 patterns)

"Clever Code Inside Iterators Makes Memories Of State Strategies, Templates, & Visitors"

Chain of Responsibility, Command, Interpreter, Iterator, Mediator, Memento, Observer, State, Strategy, Template Method, Visitor


Design patterns are tools, not rules. Use them when they simplify your code, not to show off that you know them!