# Gof Design Patterns Catalog

> Comprehensive catalog of all 23 GoF design patterns with Python implementations covering creational, structural, and behavioral patterns for decoupling and extensible software design.

- Skill: `paulpas/gof-design-patterns-catalog` (Agent Skill)
- Install (CLI): `npx skillmds@latest add paulpas/gof-design-patterns-catalog`
- Raw SKILL.md: https://api.skillmd.com/api/skills/paulpas/gof-design-patterns-catalog/raw
- Safety review: PASS (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- License: MIT
- Author: paulpas (https://skillmd.com/u/paulpas)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/paulpas/gof-design-patterns-catalog

---






# GoF Design Patterns Catalog

Senior software architect implementing all 23 Gang of Four (GoF) design patterns in Python with typed signatures and real-world examples. This skill makes the model classify problems into creational, structural, or behavioral families, select the most appropriate pattern, and produce concrete implementations that respect SOLID principles — favoring composition over inheritance, single responsibility, and open/closed extensibility.

## TL;DR Checklist

- [ ] Classify the problem: creation bottleneck → creational, composition challenge → structural, interaction complexity → behavioral
- [ ] Identify the independent change point — patterns belong where things vary
- [ ] Prefer composition and `typing.Protocol` interfaces over deep inheritance hierarchies
- [ ] Use Protocol or ABC for all pattern abstractions to enforce LSP compliance
- [ ] Keep each class responsible for exactly one reason to change (SRP)
- [ ] Validate that new behaviors can be added without modifying existing code (OCP)
- [ ] Prefer explicit `__init__` parameters over implicit global state (Singleton antipattern)

---

## When to Use

Use this skill when:

- A class is doing too much and needs to delegate responsibility to a separate pattern
- You need to add behavior to objects dynamically without creating new subclasses
- Object creation logic is scattered across many `if/elif` branches or factory functions
- Multiple classes share a common interface but have incompatible implementations (Adapter)
- You need to decouple an abstraction from its implementation so both can evolve independently (Bridge)
- A complex subsystem needs a simplified entry point for callers (Facade)
- You are designing event-driven systems with publishers and subscribers (Observer)

---

## When NOT to Use

Avoid GoF patterns when:

- Python's built-in features already solve the problem (use `@classmethod` instead of Singleton, use dataclasses/Attrs instead of Builder for simple value objects)
- A function with well-defined parameters is clearer than a pattern hierarchy (YAGNI)
- You are building a small script or prototype where the overhead exceeds the benefit
- Dependency Injection can solve the coupling problem without introducing indirection

---

## Core Workflow

1. **Classify the problem** — Determine if it is a creation bottleneck (creational), composition challenge (structural), or interaction complexity (behavioral). **Checkpoint:** Does a single pattern solve it, or do multiple patterns compose? Creational patterns answer "how to create," structural patterns answer "how to compose," behavioral patterns answer "how objects communicate."

2. **Select the most specific pattern** — Match against the GoF catalog: Creational (Factory Method, Abstract Factory, Builder, Prototype, Singleton), Structural (Adapter, Bridge, Composite, Decorator, Facade, Proxy, Flyweight), Behavioral (Observer, State, Strategy, Command, Template Method, Mediator, Chain of Responsibility, Iterator, Visitor, Memento, Interpreter). **Checkpoint:** Is the selected pattern the simplest one that solves the problem? If three patterns apply, choose the one with the fewest classes.

3. **Implement with SOLID constraints** — Write concrete implementations using `typing.Protocol` or ABCs for abstractions. Enforce Single Responsibility by ensuring each class has one reason to change and Open/Closed by designing interfaces that allow new behavior via composition rather than modification. **Checkpoint:** Can you add a new concrete strategy, decorator, or state without modifying existing code?

4. **Compose patterns where needed** — Real systems often layer multiple patterns: a Builder creates complex objects, Facade simplifies the subsystem, Observer notifies listeners of state changes. **Checkpoint:** Does the composition remain intuitive, or does indirection make the code harder to trace?

---

## Pattern Reference Guide

### Creational Patterns

#### Factory Method

Defines an interface for creating a single object but lets subclasses decide which concrete class to instantiate. The creator delegates instantiation to subclasses.

```python
from abc import ABC, abstractmethod
from typing import Protocol


class Notification(Protocol):
    def send(self, recipient: str, message: str) -> None: ...


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


class SlackNotification:
    def send(self, recipient: str, message: str) -> None:
        print(f"💬 Slack to {recipient}: {message}")


class NotificationFactory(ABC):
    @abstractmethod
    def create_notification(self) -> Notification: ...

    def notify(self, recipient: str, message: str) -> None:
        notification = self.create_notification()
        notification.send(recipient, message)


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


class SlackFactory(NotificationFactory):
    def create_notification(self) -> Notification:
        return SlackNotification()
```

Use when: Subclasses must decide the concrete product type. Not when a single factory can handle all cases — use Abstract Factory instead.

#### Abstract Factory

Creates families of related or dependent objects without specifying their concrete classes. Ideal when systems must support multiple product families (e.g., UI themes, database backends).

```python
from abc import ABC, abstractmethod
from typing import Protocol


class Button(Protocol):
    def render(self) -> str: ...


class Checkbox(Protocol):
    def render(self) -> str: ...


class ModernButton:
    def render(self) -> str: return "ModernButton"


class ModernCheckbox:
    def render(self) -> str: return "ModernCheckbox"


class RetroButton:
    def render(self) -> str: return "RetroButton"


class RetroCheckbox:
    def render(self) -> str: return "RetroCheckbox"


class GuiFactory(ABC):
    @abstractmethod
    def create_button(self) -> Button: ...

    @abstractmethod
    def create_checkbox(self) -> Checkbox: ...


class ModernGuiFactory(GuiFactory):
    def create_button(self) -> Button: return ModernButton()
    def create_checkbox(self) -> Checkbox: return ModernCheckbox()


class RetroGuiFactory(GuiFactory):
    def create_button(self) -> Button: return RetroButton()
    def create_checkbox(self) -> Checkbox: return RetroCheckbox()
```

Use when: You need families of related products. Avoid if you only create one type of object — use Factory Method instead.

#### Builder

Separates the construction of a complex object from its representation so the same construction process can create different representations. Essential for objects with many optional parameters.

```python
from typing import Optional


class HttpRequestBuilder:
    """Builds HTTP request configurations step by step."""

    def __init__(self, method: str = "GET", url: str = "") -> None:
        self._method = method
        self._url = url
        self._headers: dict[str, str] = {}
        self._body: Optional[str] = None
        self._timeout: int = 30

    def with_method(self, method: str) -> "HttpRequestBuilder":
        self._method = method.upper()
        return self

    def with_url(self, url: str) -> "HttpRequestBuilder":
        self._url = url
        return self

    def with_header(self, key: str, value: str) -> "HttpRequestBuilder":
        self._headers[key] = value
        return self

    def with_body(self, body: str) -> "HttpRequestBuilder":
        self._body = body
        return self

    def with_timeout(self, seconds: int) -> "HttpRequestBuilder":
        self._timeout = seconds
        return self

    def build(self) -> dict:
        return {
            "method": self._method,
            "url": self._url,
            "headers": self._headers,
            "body": self._body,
            "timeout": self._timeout,
        }


# Usage: request = HttpRequestBuilder().with_method("POST").with_url("/api").with_body('{"k":"v"}').build()
```

Use when: Object construction requires many optional parameters or a multi-step setup. Prefer to dataclasses for simple value objects.

#### Prototype

Creates new objects by copying an existing instance (clone) rather than constructing from scratch. Useful when object creation is expensive or complex.

```python
import copy
from typing import Any


class DocumentTemplate:
    """Expensive-to-create document that can be cloned with modifications."""

    def __init__(self, title: str, sections: list[dict[str, Any]]) -> None:
        self.title = title
        # Deep copy sections so clones are independent
        self.sections = copy.deepcopy(sections)

    def add_section(self, name: str, content: str) -> None:
        self.sections.append({"name": name, "content": content})

    def clone(self) -> "DocumentTemplate":
        return copy.deepcopy(self)

    def __repr__(self) -> str:
        return f"DocumentTemplate('{self.title}', {len(self.sections)} sections)"
```

Use when: Creating objects from scratch is expensive, or you want to avoid subclass hierarchies. Python's `copy.deepcopy` handles most Prototype use cases natively.

#### Singleton

Ensures a class has exactly one instance and provides a global access point. Use sparingly — it introduces implicit coupling and hinders testability. Prefer dependency injection for shared resources.

```python
from typing import TypeVar, Generic


T = TypeVar("T")


class Singleton(Generic[T]):
    """Thread-safe singleton base class using metaclass."""

    _instances: dict["type", object] = {}

    def __new__(cls: type[T], *args: Any, **kwargs: Any) -> T:
        if cls not in cls._instances:
            instance = super().__new__(cls)
            cls._instances[cls] = instance
        return cls._instances[cls]


class Config(Singleton):
    """Application configuration — only one instance ever."""

    def __init__(self) -> None:
        if hasattr(self, "_initialized"):
            return  # Already initialized from prior instantiation
        self._values: dict[str, str] = {}
        self._initialized = True

    def get(self, key: str, default: str = "") -> str:
        return self._values.get(key, default)

    def set(self, key: str, value: str) -> None:
        self._values[key] = value


# Usage: config = Config(); config.set("db_host", "localhost")
```

Use when: A single global resource is needed (config store, connection pool). Avoid for mutable shared state — use dependency injection instead.

---

### Structural Patterns

#### Adapter

Converts one interface to another so incompatible interfaces can work together. Wraps an existing class to match a client's expected interface.

```python
from abc import ABC, abstractmethod
from typing import Protocol


class OldPaymentGateway(ABC):
    """Third-party payment system with legacy interface."""

    @abstractmethod
    def process_payment(self, amount: float, currency: str) -> bool: ...


class NewPaymentGateway(Protocol):
    """Our standardized payment interface."""

    @abstractmethod
    def charge(self, cents: int, currency_code: str) -> bool: ...


class LegacyAdapter(NewPaymentGateway):
    """Adapts OldPaymentGateway to the NewPaymentGateway interface."""

    def __init__(self, gateway: OldPaymentGateway) -> None:
        self._gateway = gateway

    def charge(self, cents: int, currency_code: str) -> bool:
        amount = cents / 100.0
        return self._gateway.process_payment(amount, currency_code)


# Usage: adapter = LegacyAdapter(stripe_client); adapter.charge(9999, "USD")
```

Use when: Integrating third-party libraries with incompatible interfaces, or migrating between systems incrementally.

#### Bridge

Decouples an abstraction from its implementation so both can vary independently. Unlike Adapter (which fixes incompatibility after the fact), Bridge is designed in from the start.

```python
from abc import ABC, abstractmethod


class RenderingEngine(ABC):
    """Abstract rendering backend."""

    @abstractmethod
    def render_circle(self, x: float, y: float, radius: float) -> None: ...


class VectorEngine(RenderingEngine):
    def render_circle(self, x: float, y: float, radius: float) -> None:
        print(f"Drawing vector circle at ({x}, {y}) r={radius}")


class RasterEngine(RenderingEngine):
    def render_circle(self, x: float, y: float, radius: float) -> None:
        print(f"Drawing raster circle at ({x}, {y}) r={radius}")


class Shape(ABC):
    """Abstraction — composed with an implementation."""

    def __init__(self, engine: RenderingEngine) -> None:
        self._engine = engine

    @abstractmethod
    def draw(self) -> None: ...


class Circle(Shape):
    def __init__(self, x: float, y: float, radius: float, engine: RenderingEngine) -> None:
        super().__init__(engine)
        self._x = x
        self._y = y
        self._radius = radius

    def draw(self) -> None:
        self._engine.render_circle(self._x, self._y, self._radius)


# Usage: Circle(10, 20, 5, VectorEngine()).draw()  → vector circle
# Usage: Circle(10, 20, 5, RasterEngine()).draw()  → raster circle
```

Use when: Both abstraction and implementation need to evolve independently. The key distinction from Adapter: Bridge is intentional design from the start.

#### Composite

Composes objects into tree structures to represent part-whole hierarchies. Clients treat individual objects and compositions uniformly.

```python
from abc import ABC, abstractmethod
from typing import List


class Component(ABC):
    @abstractmethod
    def operation(self) -> str: ...

    @abstractmethod
    def add(self, component: "Component") -> None: ...

    @abstractmethod
    def remove(self, component: "Component") -> None: ...

    @abstractmethod
    def is_leaf(self) -> bool: ...


class Leaf(Component):
    def __init__(self, name: str) -> None:
        self._name = name

    def operation(self) -> str:
        return f"Leaf({self._name})"

    def add(self, component: Component) -> None:
        raise NotImplementedError("Leaf cannot have children")

    def remove(self, component: Component) -> None:
        raise NotImplementedError("Leaf cannot be removed from parent")

    def is_leaf(self) -> bool:
        return True


class Composite(Component):
    def __init__(self, name: str) -> None:
        self._name = name
        self._children: List[Component] = []

    def operation(self) -> str:
        results = [child.operation() for child in self._children]
        return f"Composite({self._name}: {', '.join(results)})"

    def add(self, component: Component) -> None:
        self._children.append(component)

    def remove(self, component: Component) -> None:
        self._children.remove(component)

    def is_leaf(self) -> bool:
        return False


# Usage: root = Composite("root"); root.add(Leaf("file1")); root.add(Composite("dir").add(Leaf("file2")))
```

Use when: Representing tree-like part-whole structures (file systems, UI component trees, org charts). Not for flat lists with no hierarchy.

#### Decorator

Attaches additional responsibilities to objects dynamically without altering their class. Provides a flexible alternative to subclassing for behavioral extension.

```python
from abc import ABC, abstractmethod


class Notification(ABC):
    @abstractmethod
    def send(self, recipient: str) -> str: ...


class EmailNotification(Notification):
    def send(self, recipient: str) -> str:
        return f"Email sent to {recipient}"


class NotificationDecorator(Notification):
    def __init__(self, notification: Notification) -> None:
        self._notification = notification

    def send(self, recipient: str) -> str:
        return self._notification.send(recipient)


class LoggingDecorator(NotificationDecorator):
    def send(self, recipient: str) -> str:
        print(f"→ Sending notification to {recipient}")
        result = super().send(recipient)
        print(f"← Result: {result}")
        return result


class RetryDecorator(NotificationDecorator):
    def __init__(self, notification: Notification, max_retries: int = 3) -> None:
        super().__init__(notification)
        self._max_retries = max_retries

    def send(self, recipient: str) -> str:
        for attempt in range(1, self._max_retries + 1):
            try:
                return super().send(recipient)
            except Exception as e:
                if attempt == self._max_retries:
                    raise RuntimeError(f"Failed after {self._max_retries} retries: {e}")


# Usage: decorated = RetryDecorator(LoggingDecorator(EmailNotification()))
#        decorated.send("user@example.com")
```

Use when: Adding behavior dynamically (logging, retry, caching, auth). Avoid deep decorator chains (>3 layers) — consider composition instead.

#### Facade

Provides a simplified interface to a complex subsystem. Hides the complexity of multiple interrelated classes behind one clean entry point.

```python
class MediaDecoder:
    def decode(self, data: bytes) -> dict: return {"frames": 30, "fps": 24}


class AudioProcessor:
    def normalize(self, audio: bytes) -> bytes: return b"normalized"
    def convert_format(self, audio: bytes, fmt: str) -> bytes: return f"converted_{fmt}".encode()


class VideoRenderer:
    def render(self, frames: dict) -> str: return "rendered_video_frame"


class MediaFacade:
    """Simplified interface for complex media processing pipeline."""

    def process_media(self, data: bytes, audio_format: str = "mp3") -> dict:
        decoded = MediaDecoder().decode(data)
        normalized = AudioProcessor().normalize(data)
        converted = AudioProcessor().convert_format(normalized, audio_format)
        rendered = VideoRenderer().render(decoded)
        return {
            "frames": rendered,
            "audio": converted.decode(),
            "fps": decoded["fps"],
        }


# Usage: result = MediaFacade().process_media(raw_bytes)  # single call instead of 4 subsystem calls
```

Use when: A subsystem has many interdependent classes and callers need a simplified entry point. The Facade does not encapsulate — it delegates.

#### Proxy

Controls access to another object, providing a placeholder or surrogate. Common uses: lazy initialization, access control, caching, remote references.

```python
from typing import Optional


class Image(ABC):
    @abstractmethod
    def display(self) -> None: ...


class RealImage(Image):
    def __init__(self, filepath: str) -> None:
        self._filepath = filepath
        # Simulate expensive loading
        print(f"  Loading image from {filepath}")
        self._data = f"<image_data:{filepath}>"

    def display(self) -> None:
        print(f"Displaying {self._filepath}: {self._data}")


class ImageProxy(Image):
    """Lazy-loading proxy for expensive RealImage objects."""

    def __init__(self, filepath: str) -> None:
        self._filepath = filepath
        self._real_image: Optional[RealImage] = None

    def display(self) -> None:
        if self._real_image is None:
            self._real_image = RealImage(self._filepath)
        self._real_image.display()


# Usage: proxy = ImageProxy("photo.png")  # no loading yet
#        proxy.display()  # loads on first access only
```

Use when: Object creation/access is expensive, you need access control, or lazy initialization. A Proxy controls *access* — a Facade simplifies *interface*.

#### Flyweight

Shares common state between multiple objects to reduce memory footprint. Separates intrinsic (shared, immutable) state from extrinsic (unique, caller-managed) state.

```python
from typing import Dict


class ForestType:
    """Intrinsic state — shared across all trees of the same species."""

    def __init__(self, name: str, leaf_density: float, color: str) -> None:
        self._name = name
        self._leaf_density = leaf_density
        self._color = color

    def render(self, x: float, y: float, scale: float) -> str:
        return (f"🌳 {self._name} at ({x}, {y}) scale={scale} "
                f"density={self._leaf_density:.1%} color={self._color}")


class ForestFactory:
    """Flyweight factory that caches and reuses shared forest types."""

    _forest_types: Dict[str, ForestType] = {}

    @classmethod
    def get_forest_type(cls, name: str, leaf_density: float, color: str) -> ForestType:
        key = f"{name}_{leaf_density:.2f}_{color}"
        if key not in cls._forest_types:
            cls._forest_types[key] = ForestType(name, leaf_density, color)
        return cls._forest_types[key]


# Usage: oak = ForestFactory.get_forest_type("oak", 0.85, "green")
#        pine = ForestFactory.get_forest_type("pine", 0.60, "dark_green")
```

Use when: Creating many objects with shared state, and memory is a concern. Not worth it for small object counts — the caching overhead negates savings.

---

### Behavioral Patterns

#### Observer

Defines a one-to-many dependency so when one object changes state, all dependents are notified. Foundation of event-driven architectures in Python.

```python
from typing import Callable, List


class Observer:
    """Interface for subscribers."""
    def update(self, sender: object, data: dict) -> None: ...


class Subject:
    """Maintains a list of observers and notifies them of state changes."""

    def __init__(self) -> None:
        self._observers: List[Observer] = []
        self._data: dict = {}

    def attach(self, observer: Observer) -> None:
        if observer not in self._observers:
            self._observers.append(observer)

    def detach(self, observer: Observer) -> None:
        self._observers.remove(observer)

    def notify(self) -> None:
        for observer in self._observers:
            observer.update(self, dict(self._data))

    @property
    def data(self) -> dict:
        return self._data

    @data.setter
    def data(self, value: dict) -> None:
        self._data = value
        self.notify()


class PriceAlert(Observer):
    """React to price changes."""

    def __init__(self, threshold: float) -> None:
        self._threshold = threshold

    def update(self, sender: object, data: dict) -> None:
        current_price = data.get("price", 0)
        if abs(current_price - self._threshold) < 0.01:
            print(f"⚠️ Price alert: {current_price} ≈ threshold {self._threshold}")


# Usage: subject = Subject(); observer = PriceAlert(150.0); subject.attach(observer); subject.data = {"price": 149.99}
```

Use when: One-to-many dependencies exist, event-driven systems need publishers/subscribers. Python's `asyncio` EventLoop and `functools.wraps` often provide simpler alternatives for simple cases.

#### State

Allows an object to alter its behavior when its internal state changes. The object appears to change class. Ideal for finite state machines (order lifecycle, workflow stages).

```python
from abc import ABC, abstractmethod


class OrderState(ABC):
    @abstractmethod
    def confirm(self) -> str: ...

    @abstractmethod
    def ship(self) -> str: ...

    @abstractmethod
    def cancel(self) -> str: ...

    @abstractmethod
    def deliver(self) -> str: ...


class PendingState(OrderState):
    def confirm(self) -> str: return "Processing"
    def ship(self) -> str: raise RuntimeError("Cannot ship pending order")
    def cancel(self) -> str: return "Cancelled"
    def deliver(self) -> str: raise RuntimeError("Cannot deliver pending order")


class ProcessingState(OrderState):
    def confirm(self) -> str: raise RuntimeError("Already confirmed")
    def ship(self) -> str: return "Shipped"
    def cancel(self) -> str: return "Cancelled"
    def deliver(self) -> str: raise RuntimeError("Cannot deliver processing order")


class ShippedState(OrderState):
    def confirm(self) -> str: raise RuntimeError("Already shipped")
    def ship(self) -> str: raise RuntimeError("Already shipped")
    def cancel(self) -> str: raise RuntimeError("Cannot cancel shipped order")
    def deliver(self) -> str: return "Delivered"


class CancelledState(OrderState):
    def confirm(self) -> str: raise RuntimeError("Order is cancelled")
    def ship(self) -> str: raise RuntimeError("Order is cancelled")
    def cancel(self) -> str: raise RuntimeError("Already cancelled")
    def deliver(self) -> str: raise RuntimeError("Cannot deliver cancelled order")


class Order:
    """Context — delegates behavior to current state object."""

    def __init__(self) -> None:
        self._state: OrderState = PendingState()
        self._status: str = "Pending"

    @property
    def status(self) -> str:
        return self._status

    def _transition(self, new_state_str: str) -> None:
        state_map = {
            "Processing": ProcessingState(),
            "Shipped": ShippedState(),
            "Cancelled": CancelledState(),
            "Delivered": CancelledState(),  # terminal state
        }
        self._state = state_map[new_state_str]
        self._status = new_state_str

    def confirm(self) -> None:
        self._transition(self._state.confirm())

    def ship(self) -> None:
        self._transition(self._state.ship())

    def cancel(self) -> None:
        self._transition(self._state.cancel())

    def deliver(self) -> None:
        self._transition(self._state.deliver())


# Usage: order = Order(); order.confirm() → Processing; order.ship() → Shipped; order.deliver() → Cancelled (terminal)
```

Use when: Object behavior depends on internal state and transitions between well-defined states. For complex state machines with many transitions, consider a dedicated library like `transitions`.

#### Strategy

Defines a family of algorithms, encapsulates each, and makes them interchangeable at runtime. The client chooses the strategy without knowing its internals — classic Open/Closed Principle application.

```python
from abc import ABC, abstractmethod


class PricingStrategy(ABC):
    @abstractmethod
    def calculate(self, subtotal: float) -> float: ...


class FlatDiscount(PricingStrategy):
    def __init__(self, discount_pct: float) -> None:
        self._discount_pct = discount_pct

    def calculate(self, subtotal: float) -> float:
        return subtotal * (1 - self._discount_pct / 100.0)


class TieredDiscount(PricingStrategy):
    def __init__(self) -> None:
        self._tiers = [(100, 0.05), (500, 0.10), (1000, 0.15)]

    def calculate(self, subtotal: float) -> float:
        applicable = max(tier for tier in self._tiers if subtotal >= tier[0])
        return subtotal * (1 - applicable[1])


class VolumePricing(PricingStrategy):
    def __init__(self, base_price: float) -> None:
        self._base_price = base_price

    def calculate(self, quantity: int) -> float:
        if quantity > 50:
            return self._base_price * quantity * 0.85
        elif quantity > 20:
            return self._base_price * quantity * 0.95
        return self._base_price * quantity


class ShoppingCart:
    """Context — uses any strategy without knowing which."""

    def __init__(self, strategy: PricingStrategy) -> None:
        self._strategy = strategy
        self._items: list[dict] = []

    def add_item(self, name: str, price: float, quantity: int = 1) -> None:
        self._items.append({"name": name, "price": price, "quantity": quantity})

    def total(self) -> float:
        subtotal = sum(item["price"] * item["quantity"] for item in self._items)
        return self._strategy.calculate(subtotal if isinstance(self._strategy, FlatDiscount | TieredDiscount) else 1.0)


# Usage: cart = ShoppingCart(FlatDiscount(15)); cart.total() → discounted total
```

Use when: You have multiple interchangeable algorithms and need to switch at runtime. The Strategy pattern eliminates `if/elif` chains that select algorithm behavior.

#### Command

Encapsulates a request as an object, enabling parameterized clients, queuing, logging, and undo operations. Turns method calls into first-class objects.

```python
from abc import ABC, abstractmethod


class Command(ABC):
    @abstractmethod
    def execute(self) -> str: ...

    @abstractmethod
    def undo(self) -> str: ...


class Light:
    def __init__(self, name: str) -> None:
        self._name = name

    def turn_on(self) -> str: return f"💡 {self._name} is ON"
    def turn_off(self) -> str: return f"🌑 {self._name} is OFF"


class TurnOnCommand(Command):
    def __init__(self, light: Light) -> None:
        self._light = light

    def execute(self) -> str: return self._light.turn_on()
    def undo(self) -> str: return self._light.turn_off()


class TurnOffCommand(Command):
    def __init__(self, light: Light) -> None:
        self._light = light

    def execute(self) -> str: return self._light.turn_off()
    def undo(self) -> str: return self._light.turn_on()


class RemoteControl:
    """Invoker — holds commands and executes them."""

    def __init__(self) -> None:
        self._history: list[Command] = []

    def press_button(self, command: Command) -> str:
        result = command.execute()
        self._history.append(command)
        return result

    def undo_last(self) -> str:
        if not self._history:
            return "Nothing to undo"
        last = self._history.pop()
        return last.undo()


# Usage: remote = RemoteControl(); remote.press_button(TurnOnCommand(Light("LivingRoom"))); remote.undo_last()
```

Use when: You need queuing, logging, or undo for operations. Every method call that needs these features is a candidate.

#### Template Method

Defines the skeleton of an algorithm in a base class but lets subclasses override specific steps without changing the overall structure. Classic inheritance-based pattern — prefer composition when possible.

```python
from abc import ABC, abstractmethod


class ReportGenerator(ABC):
    """Template Method: fixed pipeline, customizable data source."""

    def generate(self) -> list[str]:
        self._open_file()
        data = self._fetch_data()
        processed = self._transform(data)
        output = self._render(processed)
        self._close_file()
        return output

    def _open_file(self) -> None:
        print(f"Opening report file")

    @abstractmethod
    def _fetch_data(self) -> list[dict]: ...

    def _transform(self, data: list[dict]) -> list[dict]:
        """Default transform — override for custom logic."""
        return [row for row in data if row.get("active", False)]

    @abstractmethod
    def _render(self, data: list[dict]) -> list[str]: ...

    def _close_file(self) -> None:
        print(f"Closing report file")


class SalesReport(ReportGenerator):
    def _fetch_data(self) -> list[dict]:
        return [{"name": "Widget A", "active": True, "revenue": 1000},
                {"name": "Widget B", "active": False, "revenue": 500}]

    def _render(self, data: list[dict]) -> list[str]:
        return [f"Sales Report: {d['name']} → ${d['revenue']}" for d in data]


# Usage: report = SalesReport(); report.generate()  # follows fixed pipeline
```

Use when: Multiple classes share the same algorithm skeleton but differ in specific steps. In Python, prefer function composition and `functools.partial` as alternatives to inheritance.

#### Mediator

Centralizes complex communications and control between related objects. Reduces coupling by preventing objects from referencing each other directly — they communicate through the mediator instead.

```python
from typing import List


class ChatMediator:
    """Central hub for chat room participants."""

    def __init__(self) -> None:
        self._participants: List["ChatParticipant"] = []

    def register(self, participant: "ChatParticipant") -> None:
        if participant not in self._participants:
            self._participants.append(participant)

    def send_message(self, sender: "ChatParticipant", message: str) -> None:
        for p in self._participants:
            if p is not sender:
                p.receive(sender.name, message)


class ChatParticipant:
    """Colleague — communicates through the mediator."""

    def __init__(self, name: str, mediator: ChatMediator) -> None:
        self.name = name
        self._mediator = mediator

    def send(self, message: str) -> None:
        print(f"[{self.name} sends]: {message}")
        self._mediator.send_message(self, message)

    def receive(self, sender_name: str, message: str) -> None:
        print(f"  [{self.name} receives from {sender_name}]: {message}")


# Usage: mediator = ChatMediator()
#        alice = ChatParticipant("Alice", mediator); bob = ChatParticipant("Bob", mediator)
#        mediator.register(alice); mediator.register(bob)
#        alice.send("Hello!")  # Bob receives it through the mediator
```

Use when: Many objects communicate with many other objects (many-to-many). Not for simple publish/subscribe — use Observer instead. Mediator is for direct, controlled routing.

#### Chain of Responsibility

Passes a request along a chain of handlers. Each handler decides either to process the request or pass it to the next handler in the chain. Ideal for logging pipelines, authentication stacks, and middleware.

```python
from abc import ABC, abstractmethod
from typing import Optional


class Handler(ABC):
    def __init__(self) -> None:
        self._next: Optional[Handler] = None

    def set_next(self, handler: "Handler") -> "Handler":
        self._next = handler
        return handler  # enable fluent chaining

    def handle(self, request: str) -> Optional[str]:
        if self._next:
            return self._next.handle(request)
        return None


class AuthHandler(Handler):
    def handle(self, request: str) -> Optional[str]:
        if not request.startswith("Bearer "):
            return "401 Unauthorized: missing token"
        token = request[7:]
        if not token.isalnum():
            return "403 Forbidden: invalid token"
        return super().handle(request)  # pass to next


class LoggingHandler(Handler):
    def handle(self, request: str) -> Optional[str]:
        print(f"📋 Processing: {request}")
        return super().handle(request)


class RateLimitHandler(Handler):
    def __init__(self, max_per_minute: int = 60) -> None:
        super().__init__()
        self._max = max_per_minute
        self._count = 0

    def handle(self, request: str) -> Optional[str]:
        self._count += 1
        if self._count > self._max:
            return "429 Too Many Requests"
        return super().handle(request)


class ResponseHandler(Handler):
    def handle(self, request: str) -> Optional[str]:
        return f"200 OK: handled {request}"


# Usage: chain = LoggingHandler().set_next(RateLimitHandler()).set_next(ResponseHandler())
#        chain.handle("Bearer abc123")  # passes through each handler in order
```

Use when: Multiple handlers may process a request and the chain needs to be configurable at runtime. Python's `functools.wraps` middleware decorators provide an alternative for web frameworks.

#### Iterator

Provides sequential access to elements of a collection without exposing its underlying representation. Python's built-in iteration protocol (`__iter__`, `__next__`) makes explicit Iterator classes unnecessary in most cases.

```python
from typing import Iterator, TypeVar


T = TypeVar("T")


class Book:
    def __init__(self, title: str, pages: int) -> None:
        self.title = title
        self.pages = pages


class BookShelf:
    """Custom collection with explicit iteration protocol."""

    def __init__(self) -> None:
        self._books: list[Book] = []

    def add_book(self, book: Book) -> None:
        self._books.append(book)

    def __iter__(self) -> Iterator[Book]:
        return iter(self._books)  # delegate to built-in iterator


class ReverseBookShelf(BookShelf):
    """Custom reverse iteration — demonstrates custom protocol."""

    def __iter__(self) -> Iterator[Book]:
        return reversed(self._books)


# Usage: shelf = BookShelf(); shelf.add_book(Book("1984", 328))
#        for book in shelf: print(book.title)
#        for book in ReverseBookShelf(): print(book.title)  # reverse order
```

Use when: Building custom collections where the iteration order is non-trivial or needs to be varied. Python's built-in `__iter__` protocol handles most cases natively.

#### Visitor

Represents an operation to be performed on elements of a structure, allowing new operations without changing element classes. Enables double dispatch in single-dispatch languages like Python.

```python
from abc import ABC, abstractmethod


class Element(ABC):
    @abstractmethod
    def accept(self, visitor: "Visitor") -> None: ...


class Folder(Element):
    def __init__(self, name: str) -> None:
        self.name = name
        self.children: list[Element] = []

    def add(self, child: Element) -> None:
        self.children.append(child)

    def accept(self, visitor: "Visitor") -> None:
        visitor.visit_folder(self)
        for child in self.children:
            child.accept(visitor)


class File(Element):
    def __init__(self, name: str, size_bytes: int) -> None:
        self.name = name
        self.size_bytes = size_bytes

    def accept(self, visitor: "Visitor") -> None:
        visitor.visit_file(self)


class Visitor(ABC):
    @abstractmethod
    def visit_folder(self, folder: Folder) -> None: ...

    @abstractmethod
    def visit_file(self, file: File) -> None: ...


class SizeCalculator(Visitor):
    def __init__(self) -> None:
        self._total_bytes = 0

    def visit_folder(self, folder: Folder) -> None:
        pass  # accumulate from children via their accept() calls

    def visit_file(self, file: File) -> None:
        self._total_bytes += file.size_bytes

    @property
    def total_bytes(self) -> int:
        return self._total_bytes


class NameCollector(Visitor):
    def __init__(self) -> None:
        self._names: list[str] = []

    def visit_folder(self, folder: Folder) -> None:
        self._names.append(f"[DIR] {folder.name}")

    def visit_file(self, file: File) -> None:
        self._names.append(f"  {file.name} ({file.size_bytes} bytes)")


# Usage: root = Folder("project")
#        root.add(File("main.py", 2048)); root.add(Folder("src").add(File("app.py", 512)))
#        calc = SizeCalculator(); root.accept(calc); print(calc.total_bytes)
```

Use when: You need to perform many different operations on a fixed class hierarchy without modifying those classes. For simple traversals, use `for` loops directly.

#### Memento

Captures and externalizes an object's internal state so it can be restored later. The canonical implementation of undo functionality. The Caretaker (caller) holds the memento but never inspects its contents — only the Originator can read/write them.

```python
from typing import Any


class EditorState:
    """Memento — stores internal state, immutable from outside."""

    def __init__(self, content: str, cursor_pos: int, undo_stack_size: int) -> None:
        self._content = content
        self._cursor_pos = cursor_pos
        self._undo_stack_size = undo_stack_size

    @property
    def content(self) -> str: return self._content
    @property
    def cursor_pos(self) -> int: return self._cursor_pos


class TextEditor:
    """Originator — creates and restores mementos."""

    def __init__(self) -> None:
        self._content: str = ""
        self._cursor_pos: int = 0

    def type(self, text: str) -> None:
        self._content += text
        self._cursor_pos = len(self._content)

    def create_memento(self) -> EditorState:
        return EditorState(self._content, self._cursor_pos, 0)

    def restore_memento(self, memento: EditorState) -> None:
        self._content = memento.content
        self._cursor_pos = memento.cursor_pos

    @property
    def content(self) -> str:
        return self._content


class UndoManager:
    """Caretaker — stores and retrieves mementos without inspecting them."""

    def 

…(truncated)
