# Design Patterns And Principles

> Implements and explains GoF design patterns (Factory, Observer, Strategy, Decorator, Singleton), SOLID principles, and DRY/YAGNI guidelines to produce maintainable, extensible software architecture.

- Skill: `paulpas/design-patterns-and-principles` (Agent Skill)
- Install (CLI): `npx skillmds@latest add paulpas/design-patterns-and-principles`
- Raw SKILL.md: https://api.skillmd.com/api/skills/paulpas/design-patterns-and-principles/raw
- Safety review: pending
- 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/design-patterns-and-principles

---





# Design Patterns & Software Architecture Principles

Senior software architect designing maintainable, extensible systems using proven design patterns and principles. Applies GoF patterns, SOLID principles, and DRY/YAGNI guidelines to produce code that is easy to understand, test, and evolve. Evaluates architectural tradeoffs, prevents over-engineering, and selects the simplest pattern that solves the identified problem.

## TL;DR Checklist

- [ ] Identify the core problem and change point before reaching for a pattern
- [ ] Prefer composition over inheritance (SOLID - Open/Closed Principle)
- [ ] Apply Single Responsibility: each class has one reason to change
- [ ] Verify Dependence Inversion: high-level modules depend on abstractions, not concretions
- [ ] Avoid over-engineering — YAGNI means don't add abstractions prematurely
- [ ] Eliminate duplication: DRY means abstract shared behavior, not repeat it
- [ ] Choose the simplest pattern that solves the problem
- [ ] Ensure every pattern has a concrete problem it addresses — no pattern for pattern's sake

---

## When to Use

Use this skill when:

- Designing new modules or services from scratch and needing structural guidance
- Refactoring legacy code that is hard to test or extend
- Resolving tight coupling between components that prevents independent changes
- Deciding between inheritance and composition for a new abstraction
- Introducing testability into code that lacks dependency injection
- Evaluating whether a proposed abstraction is warranted or premature (YAGNI check)
- Resolving a conditional explosion of if/elif/else branches that encode algorithm selection
- Managing cross-cutting concerns that would otherwise scatter code across many classes

---

## When NOT to Use

Avoid applying design patterns when:

- Building a simple script or one-off tool where no extension is expected (YAGNI applies)
- A standard library function or language feature already solves the problem cleanly
- For data structures that don't require behavioral flexibility (don't over-engineer)
- The team lacks the experience to maintain the chosen pattern correctly
- The added complexity of a pattern outweighs the benefit of the change it enables
- You are introducing an abstraction without a concrete second use case

---

## Core Workflow

1. **Analyze Requirements** — Identify what changes, what stays stable, and what abstractions are genuinely needed vs. speculative. Map the change points in the system.
   **Checkpoint:** Can the problem be solved without any abstraction? If yes, YAGNI applies — skip the pattern.

2. **Apply SOLID Principles** — Evaluate the design against each principle:
   - SRP: Does each class have a single responsibility?
   - OCP: Can new behavior be added without modifying existing code?
   - LSP: Will subclasses be substitutable for base classes?
   - ISP: Are interfaces slim and client-specific?
   - DIP: Do high-level modules depend on abstractions, not concretions?
   **Checkpoint:** If more than one principle is violated, the design needs restructuring before selecting a pattern.

3. **Select a Pattern** — Match the problem to the appropriate GoF category:
   - Creational (object creation): Factory Method, Builder, Singleton, Abstract Factory, Prototype
   - Structural (composition): Adapter, Decorator, Facade, Proxy, Composite, Bridge, Flyweight
   - Behavioral (communication): Strategy, Observer, Command, Iterator, State, Template Method, Mediator, Memento, Visitor
   **Checkpoint:** The pattern must address the actual change point identified in step 1.

4. **Implement with DRY** — Extract shared behavior. Avoid duplication by factoring out common logic into well-named, single-responsibility components.
   **Checkpoint:** Run the duplication test — if the same logic appears in two places with only minor differences, it should be abstracted.

5. **Review & Validate** — Check against all constraints below. Ensure the design can be unit-tested in isolation. Verify no hierarchy exceeds 3 levels.
   **Checkpoint:** Can each concrete class be tested independently? If not, DIP has not been applied correctly.

---

## Implementation Patterns — GoF Creational

### Factory Method

The Factory Method defines an interface for creating objects, but lets subclasses or a factory class decide which concrete class to instantiate. This decouples the client from concrete implementations.

```python
# ❌ BAD — direct instantiation couples client to concrete classes
class OrderProcessor:
    def process(self, payment_type: str) -> None:
        if payment_type == "credit_card":
            gateway = CreditCardGateway()
        elif payment_type == "paypal":
            gateway = PayPalGateway()
        elif payment_type == "crypto":
            gateway = CryptoGateway()
        gateway.charge(100.0)
```

```python
# ✅ GOOD — Factory Method enables adding new payment types without modification
from abc import ABC, abstractmethod

class PaymentGateway(ABC):
    """Abstract product — all payment gateways share this interface."""

    @abstractmethod
    def charge(self, amount: float) -> bool:
        """Process a payment and return success status."""
        ...


class CreditCardGateway(PaymentGateway):
    def charge(self, amount: float) -> bool:
        # Process credit card payment
        return True

class PayPalGateway(PaymentGateway):
    def charge(self, amount: float) -> bool:
        # Process PayPal payment
        return True

class CryptoGateway(PaymentGateway):
    def charge(self, amount: float) -> bool:
        # Process cryptocurrency payment
        return True


class PaymentFactory:
    @staticmethod
    def create_gateway(payment_type: str) -> PaymentGateway:
        factories: dict[str, type[PaymentGateway]] = {
            "credit_card": CreditCardGateway,
            "paypal": PayPalGateway,
            "crypto": CryptoGateway,
        }
        factory_cls = factories.get(payment_type)
        if factory_cls is None:
            raise ValueError(f"Unknown payment type: {payment_type}")
        return factory_cls()


class OrderProcessor:
    def __init__(self, factory: PaymentFactory | None = None):
        self._factory = factory or PaymentFactory()

    def process(self, payment_type: str, amount: float) -> None:
        gateway = self._factory.create_gateway(payment_type)
        gateway.charge(amount)
```

**Why this works:** Adding a new payment type requires only adding a new class implementing `PaymentGateway` and registering it in the factory dictionary. `OrderProcessor` never changes — satisfying the Open/Closed Principle.

---

### Builder

The Builder pattern separates complex object construction from its representation. Useful when an object requires many configuration parameters, some optional, and the construction process has multiple steps.

```python
# ❌ BAD — telescoping constructor makes objects hard to create correctly
class HttpRequest:
    def __init__(
        self, method: str, url: str, headers: dict | None = None,
        body: str | None = None, timeout: int = 30,
        retry_count: int = 0, verify_ssl: bool = True,
        proxy: str | None = None, auth: tuple | None = None,
    ):
        # With 10 parameters, remembering which is which is error-prone
        self.method = method
        self.url = url
        self.headers = headers or {}
        self.body = body
        self.timeout = timeout
        self.retry_count = retry_count
        self.verify_ssl = verify_ssl
        self.proxy = proxy
        self.auth = auth
```

```python
# ✅ GOOD — Builder provides fluent, readable construction
class HttpRequest:
    def __init__(
        self, method: str, url: str,
        headers: dict | None = None, body: str | None = None,
        timeout: int = 30, retry_count: int = 0,
        verify_ssl: bool = True, proxy: str | None = None,
        auth: tuple | None = None,
    ):
        self.method = method
        self.url = url
        self.headers = headers or {}
        self.body = body
        self.timeout = timeout
        self.retry_count = retry_count
        self.verify_ssl = verify_ssl
        self.proxy = proxy
        self.auth = auth


class HttpRequestBuilder:
    def __init__(self, method: str, url: str) -> None:
        self._method = method
        self._url = url
        self._headers: dict = {}
        self._body: str | None = None
        self._timeout: int = 30
        self._retry_count: int = 0
        self._verify_ssl: bool = True
        self._proxy: str | None = None
        self._auth: tuple | None = None

    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 with_retry(self, count: int) -> "HttpRequestBuilder":
        self._retry_count = count
        return self

    def with_ssl_verification(self, verify: bool) -> "HttpRequestBuilder":
        self._verify_ssl = verify
        return self

    def with_proxy(self, proxy_url: str) -> "HttpRequestBuilder":
        self._proxy = proxy_url
        return self

    def with_auth(self, username: str, password: str) -> "HttpRequestBuilder":
        self._auth = (username, password)
        return self

    def build(self) -> HttpRequest:
        return HttpRequest(
            method=self._method, url=self._url,
            headers=self._headers, body=self._body,
            timeout=self._timeout, retry_count=self._retry_count,
            verify_ssl=self._verify_ssl, proxy=self._proxy,
            auth=self._auth,
        )


# Usage — clear and readable:
# request = (HttpRequestBuilder("POST", "https://api.example.com/data")
#            .with_header("Content-Type", "application/json")
#            .with_body('{"key": "value"}')
#            .with_timeout(60)
#            .with_retry(3)
#            .build())
```

---

### Singleton (Anti-Pattern Warning)

Singleton ensures a class has only one instance and provides global access. This skill covers Singleton for reference, but **strongly discourages its use** — it hides dependencies, prevents testing, and creates implicit coupling. Use dependency injection instead.

```python
# ❌ BAD — Singleton hides dependencies and makes testing impossible
class DatabaseConnection:
    _instance: "DatabaseConnection | None" = None

    def __new__(cls) -> "DatabaseConnection":
        if cls._instance is None:
            cls._instance = super().__new__(cls)
            cls._instance._connect()
        return cls._instance

    def _connect(self) -> None:
        self._connection = create_connection("postgres://localhost/mydb")

    def query(self, sql: str) -> list[dict]:
        return self._connection.execute(sql)


# Cannot inject a mock — all code shares the same real database connection
db = DatabaseConnection()  # Always the same instance
```

```python
# ✅ GOOD — Dependency Injection replaces Singleton
# The calling code manages the single instance and injects it.
class DatabaseConnection:
    def __init__(self, connection_string: str):
        self._connection = create_connection(connection_string)

    def query(self, sql: str) -> list[dict]:
        return self._connection.execute(sql)


# App startup — single instance created once, injected everywhere:
# db = DatabaseConnection("postgres://localhost/mydb")
# router = create_router(database=db)  # Injected, testable
```

**Rule of thumb:** If you reach for Singleton, ask: "Can I manage this as a singleton at the composition root instead?" The answer is almost always yes.

---

### Abstract Factory

Abstract Factory provides an interface for creating families of related objects without specifying their concrete classes. Useful when your system must work with multiple product families (e.g., UI themes, database backends).

```python
# ❌ BAD — Concrete factory methods scattered across the codebase
def create_button(theme: str) -> Button:
    if theme == "dark":
        return DarkButton()
    elif theme == "light":
        return LightButton()
    raise ValueError(f"Unknown theme: {theme}")


# ✅ GOOD — Abstract Factory creates cohesive product families
from abc import ABC, abstractmethod

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

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


class DarkButton(Button):
    def render(self) -> str: return "<button class='dark'>"
    def click(self) -> str: return "dark button clicked"

class LightButton(Button):
    def render(self) -> str: return "<button class='light'>"
    def click(self) -> str: return "light button clicked"

class DarkCheckbox(Checkbox):
    def render(self) -> str: return "<checkbox class='dark'>"
    def toggle(self) -> str: return "dark checkbox toggled"

class LightCheckbox(Checkbox):
    def render(self) -> str: return "<checkbox class='light'>"
    def toggle(self) -> str: return "light checkbox toggled"


class UIFactory(ABC):
    """Abstract Factory — creates a family of UI components."""

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


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

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


# Client code works with abstract factories only
class SettingsPanel:
    def __init__(self, factory: UIFactory):
        self._factory = factory

    def render(self) -> str:
        button = self._factory.create_button()
        checkbox = self._factory.create_checkbox()
        return f"{button.render()} {checkbox.render()}"

# Usage — swap entire theme by changing the factory:
# panel = SettingsPanel(DarkUIFactory())
```

---

### Prototype

The Prototype pattern creates new objects by copying an existing instance (the prototype). Useful when object creation is expensive or when you need to avoid subclassing to create instances of varying types.

```python
# ✅ GOOD — Prototype pattern for expensive object creation
from copy import deepcopy
from dataclasses import dataclass, field
from typing import Any


@dataclass
class ConfigTemplate:
    """A prototype configuration that can be cloned and customized."""
    host: str = "localhost"
    port: int = 8080
    debug: bool = False
    features: list[str] = field(default_factory=list)
    overrides: dict[str, Any] = field(default_factory=dict)

    def clone(self, **overrides: Any) -> "ConfigTemplate":
        """Create a deep copy with selective overrides applied."""
        cloned = deepcopy(self)
        cloned.overrides.update(overrides)
        return cloned


# Usage — create many variations from a single template:
# production_config = ConfigTemplate(host="prod.example.com", debug=False)
# staging_config = production_config.clone(host="staging.example.com", debug=True)
# dev_config = production_config.clone(host="localhost", debug=True,
#                                       features=["hot_reload", "profiler"])
```

---

## Implementation Patterns — GoF Structural

### Decorator

The Decorator pattern attaches additional responsibilities to objects dynamically. It provides a flexible alternative to subclassing for adding behavior. Ideal for cross-cutting concerns like logging, caching, or authentication.

```python
# ❌ BAD — inheritance for cross-cutting concerns creates a combinatorial explosion
class LoggedDatabaseConnection:
    def __init__(self, host: str):
        self._connection = connect(host)

    def query(self, sql: str) -> list[dict]:
        log(f"Executing: {sql}")
        return self._connection.query(sql)

class CachedLoggedDatabaseConnection(LoggedDatabaseConnection):
    def __init__(self, host: str):
        super().__init__(host)
        self._cache: dict[str, list[dict]] = {}

    def query(self, sql: str) -> list[dict]:
        if sql in self._cache:
            return self._cache[sql]
        result = super().query(sql)
        self._cache[sql] = result
        return result

# Adding AuthenticationDecorator on top requires a new class for every combination.
# With N concerns and M levels of nesting, you get N! combinations.
```

```python
# ✅ GOOD — Decorator composes cross-cutting concerns cleanly
from abc import ABC, abstractmethod


class DatabaseConnection(ABC):
    @abstractmethod
    def query(self, sql: str) -> list[dict]: ...


class RealDatabaseConnection(DatabaseConnection):
    def __init__(self, host: str) -> None:
        self._connection = connect(host)

    def query(self, sql: str) -> list[dict]:
        return self._connection.query(sql)


class LoggingDecorator(DatabaseConnection):
    def __init__(self, wrapped: DatabaseConnection) -> None:
        self._wrapped = wrapped

    def query(self, sql: str) -> list[dict]:
        log(f"Executing: {sql}")
        return self._wrapped.query(sql)


class CacheDecorator(DatabaseConnection):
    def __init__(self, wrapped: DatabaseConnection) -> None:
        self._wrapped = wrapped
        self._cache: dict[str, list[dict]] = {}

    def query(self, sql: str) -> list[dict]:
        if sql in self._cache:
            return self._cache[sql]
        result = self._wrapped.query(sql)
        self._cache[sql] = result
        return result


class TimingDecorator(DatabaseConnection):
    def __init__(self, wrapped: DatabaseConnection) -> None:
        self._wrapped = wrapped

    def query(self, sql: str) -> list[dict]:
        import time
        start = time.monotonic()
        result = self._wrapped.query(sql)
        log(f"Query took {time.monotonic() - start:.4f}s")
        return result


# Usage — compose at runtime in any order:
# conn = TimingDecorator(CacheDecorator(LoggingDecorator(RealDatabaseConnection("localhost"))))
# results = conn.query("SELECT * FROM users")
```

---

### Adapter

The Adapter pattern converts the interface of a class into another interface clients expect. It allows incompatible interfaces to work together, wrapping legacy or third-party code.

```python
# ❌ BAD — modifying existing code to fit a new interface
class LegacyPaymentAPI:
    def process_transaction(self, credit_card_number: str, amount: float, currency: str) -> bool:
        """Legacy API with an awkward interface."""
        # ... complex legacy logic ...
        return True


# After adding a new requirement, we're forced to modify or duplicate the wrapper:
class PaymentService:
    def __init__(self):
        self._api = LegacyPaymentAPI()

    def process_payment(self, card: str, amount: float, currency: str) -> bool:
        # We can use the legacy API, but the interface mismatch is ugly
        return self._api.process_transaction(card, amount, currency)


# ✅ GOOD — Adapter cleanly separates the interface mismatch
from abc import ABC, abstractmethod


class PaymentProcessor(ABC):
    """New, clean interface."""

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


class LegacyPaymentAdapter(PaymentProcessor):
    """Adapts the legacy API to the new interface."""

    def __init__(self) -> None:
        self._legacy = LegacyPaymentAPI()

    def process(self, card_number: str, amount: float, currency: str) -> bool:
        # Adapt the call to the legacy API's signature
        return self._legacy.process_transaction(card_number, amount, currency)


class StripePaymentProcessor(PaymentProcessor):
    """New, native implementation."""

    def process(self, card_number: str, amount: float, currency: str) -> bool:
        # Stripe-specific implementation
        return stripe.charge(card_number, amount, currency)


class PaymentService:
    def __init__(self, processor: PaymentProcessor):
        self._processor = processor

    def process_payment(self, card: str, amount: float, currency: str) -> bool:
        return self._processor.process(card, amount, currency)
```

---

### Facade

The Facade pattern provides a simplified interface to a complex subsystem. It hides complexity and decouples clients from the subsystem's classes.

```python
# ❌ BAD — client must understand and coordinate many subsystem classes
class ImageProcessingClient:
    def process_and_upload(self, image_path: str) -> str:
        # Client must know about every subsystem:
        raw = ImageLoader(image_path).load()
        resized = ImageResizer(raw).resize(800, 600)
        compressed = ImageCompressor(resized).compress(0.8)
        watermark = Watermarker(compressed).apply("logo.png")
        encoded = ImageEncoder(watermark, "webp").encode()
        url = StorageUploader(encoded).upload()
        return url


# ✅ GOOD — Facade provides a single, simple entry point
from abc import ABC, abstractmethod


class ImageFacade:
    """Simplified interface for the entire image processing pipeline."""

    def process_and_upload(self, image_path: str, max_width: int = 800) -> str:
        raw = ImageLoader(image_path).load()
        resized = ImageResizer(raw).resize(max_width, int(max_width * 0.75))
        compressed = ImageCompressor(resized).compress(0.8)
        watermark = Watermarker(compressed).apply("logo.png")
        encoded = ImageEncoder(watermark, "webp").encode()
        return StorageUploader(encoded).upload()


# Client code is now one line:
# url = ImageFacade().process_and_upload("photo.jpg")
```

---

### Proxy

The Proxy pattern provides a placeholder or surrogate for another object to control access to it. Useful for lazy initialization, access control, caching, and logging.

```python
# ✅ GOOD — Lazy initialization proxy for expensive object creation
from abc import ABC, abstractmethod


class Document(ABC):
    @abstractmethod
    def render(self) -> str: ...


class ExpensiveDocument(Document):
    """Expensive to create — reads from disk, parses content."""

    def __init__(self, filepath: str) -> None:
        self._filepath = filepath
        # Simulate expensive initialization
        import time; time.sleep(0.1)
        self._content = self._load_content(filepath)

    def _load_content(self, filepath: str) -> str:
        with open(filepath, "r") as f:
            return f.read()

    def render(self) -> str:
        return f"[Document: {self._filepath}]\n{self._content}"


class DocumentProxy(Document):
    """Proxy that defers document loading until first access."""

    def __init__(self, filepath: str) -> None:
        self._filepath = filepath
        self._document: ExpensiveDocument | None = None

    def render(self) -> str:
        if self._document is None:
            self._document = ExpensiveDocument(self._filepath)
        return self._document.render()


# The ExpensiveDocument is only created when render() is first called:
# doc = DocumentProxy("large_file.txt")  # No expense yet
# print(doc.render())  # Expensive load happens here
# print(doc.render())  # Reuses the already-loaded document
```

---

### Composite

The Composite pattern lets you compose objects into tree structures and treat individual objects and compositions uniformly. Ideal for UI component trees, filesystems, and organizational hierarchies.

```python
# ✅ GOOD — Treat leaves and composites uniformly
from abc import ABC, abstractmethod


class Component(ABC):
    @abstractmethod
    def render(self, indent: int = 0) -> str: ...
    @abstractmethod
    def get_cost(self) -> float: ...


class Leaf(Component):
    """An indivisible component."""

    def __init__(self, name: str, cost: float) -> None:
        self._name = name
        self._cost = cost

    def render(self, indent: int = 0) -> str:
        prefix = "  " * indent
        return f"{prefix}- {self._name} (${self._cost:.2f})"

    def get_cost(self) -> float:
        return self._cost


class Composite(Component):
    """A container of components."""

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

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

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

    def render(self, indent: int = 0) -> str:
        lines = [f"{'  ' * indent}+ {self._name}"]
        for child in self._children:
            lines.append(child.render(indent + 1))
        return "\n".join(lines)

    def get_cost(self) -> float:
        return sum(child.get_cost() for child in self._children)


# Build a project structure:
# project = Composite("E-Commerce Platform")
# project.add(Leaf("Backend API", 5000))
# project.add(Leaf("Frontend", 4000))
#
# frontend = Composite("Frontend")
# frontend.add(Leaf("React Components", 2500))
# frontend.add(Leaf("Styling", 1500))
# project.add(frontend)
#
# print(project.render())
# print(f"Total cost: ${project.get_cost():.2f}")  # $11,000.00
```

---

### Bridge

The Bridge pattern decouples an abstraction from its implementation so that both can vary independently. Unlike composition (where the implementation is chosen at construction), Bridge allows runtime swapping.

```python
# ✅ GOOD — Drawing abstraction independent of rendering implementation
from abc import ABC, abstractmethod


class Renderer(ABC):
    """Implementation hierarchy."""

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

    @abstractmethod
    def render_rectangle(self, x: float, y: float, w: float, h: float) -> str: ...


class VectorRenderer(Renderer):
    def render_circle(self, x: float, y: float, radius: float) -> str:
        return f"<circle cx='{x}' cy='{y}' r='{radius}' />"

    def render_rectangle(self, x: float, y: float, w: float, h: float) -> str:
        return f"<rect x='{x}' y='{y}' width='{w}' height='{h}' />"


class RasterRenderer(Renderer):
    def render_circle(self, x: float, y: float, radius: float) -> str:
        return f"bitmap circle at ({x},{y}) radius {radius}"

    def render_rectangle(self, x: float, y: float, w: float, h: float) -> str:
        return f"bitmap rect at ({x},{y}) size {w}x{h}"


class Shape(ABC):
    """Abstraction — depends on Renderer, not on specific implementations."""

    def __init__(self, renderer: Renderer) -> None:
        self._renderer = renderer

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

    @abstractmethod
    def resize(self, factor: float) -> None: ...


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

    def draw(self) -> str:
        return self._renderer.render_circle(self._x, self._y, self._radius)

    def resize(self, factor: float) -> None:
        self._radius *= factor


class Rectangle(Shape):
    def __init__(self, x: float, y: float, w: float, h: float, renderer: Renderer) -> None:
        super().__init__(renderer)
        self._x = x
        self._y = y
        self._w = w
        self._h = h

    def draw(self) -> str:
        return self._renderer.render_rectangle(self._x, self._y, self._w, self._h)

    def resize(self, factor: float) -> None:
        self._w *= factor
        self._h *= factor


# Swap implementations at runtime:
# vector_circle = Circle(10, 20, 5, VectorRenderer())
# raster_circle = Circle(10, 20, 5, RasterRenderer())
```

---

### Flyweight

The Flyweight pattern minimizes memory usage by sharing common state between objects. Useful when you need to create large numbers of fine-grained objects that share data (e.g., characters in a text editor, game entities with shared visual assets).

```python
# ✅ GOOD — Share intrinsic state to reduce memory footprint
from dataclasses import dataclass


@dataclass(frozen=True)
class ChessPieceType:
    """Intrinsic state — shared across all pieces of a given type."""
    name: str
    symbol: str
    value: int


class ChessPiece:
    """Extrinsic state (position) is separate from intrinsic state (type)."""

    # Pool of shared flyweight objects
    _cache: dict[str, ChessPieceType] = {}

    @classmethod
    def get_type(cls, name: str) -> ChessPieceType:
        if name not in cls._cache:
            symbols = {"pawn": "♙", "rook": "♖", "knight": "♘", "bishop": "♗", "queen": "♕", "king": "♔"}
            values = {"pawn": 1, "rook": 5, "knight": 3, "bishop": 3, "queen": 9, "king": 0}
            cls._cache[name] = ChessPieceType(name, symbols[name], values[name])
        return cls._cache[name]

    def __init__(self, piece_type: ChessPieceType, row: int, col: int) -> None:
        self._type = piece_type   # Shared (intrinsic)
        self._row = row           # Unique (extrinsic)
        self._col = col           # Unique (extrinsic)

    def display(self) -> str:
        return f"{self._type.symbol}({self._row},{self._col})"


# Creating 64 pieces on a board — only 6 ChessPieceType objects are created:
# board: list[ChessPiece] = []
# for col in range(8):
#     board.append(ChessPiece(ChessPieceType.get_type("pawn"), 1, col))
# board.append(ChessPiece(ChessPieceType.get_type("rook"), 0, 0))
# ...
```

---

## Implementation Patterns — GoF Behavioral

### Strategy

The Strategy pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. The client selects the strategy at runtime. Replaces conditional logic with polymorphism.

```python
# ❌ BAD — conditional logic for algorithm selection is hard to test and extend
class DiscountService:
    def calculate_discount(self, customer: dict) -> float:
        if customer["type"] == "premium":
            return customer["subtotal"] * 0.15
        elif customer["type"] == "standard":
            return customer["subtotal"] * 0.05
        elif customer["type"] == "vip":
            return customer["subtotal"] * 0.25
        return 0.0


# ✅ GOOD — Strategy pattern makes discount rules open/closed and individually testable
from abc import ABC, abstractmethod
from typing import Protocol


class DiscountStrategy(Protocol):
    def calculate(self, subtotal: float) -> float: ...


class PremiumDiscount:
    def calculate(self, subtotal: float) -> float:
        return subtotal * 0.15


class StandardDiscount:
    def calculate(self, subtotal: float) -> float:
        return subtotal * 0.05


class VIPDiscount:
    def calculate(self, subtotal: float) -> float:
        return subtotal * 0.25


class DiscountService:
    def __init__(self, strategy: DiscountStrategy) -> None:
        self._strategy = strategy

    def apply_discount(self, subtotal: float) -> float:
        return self._strategy.calculate(subtotal)


# Usage — swap strategies at runtime:
# service = DiscountService(PremiumDiscount())
# discount = service.apply_discount(200.0)  # returns 30.0
# service = DiscountService(VIPDiscount())   # swap strategy
# discount = service.apply_discount(200.0)  # returns 50.0
```

---

### Observer

The Observer pattern defines a one-to-many dependency between objects so that when one object changes state, all its dependents are notified. Replaces manual notification chains with a publish-subscribe mechanism.

```python
# ❌ BAD — manual notification is error-prone and easily forgotten
class OrderManager:
    def __init__(self) -> None:
        self._email_sender = EmailSender()
        self._inventory_service = InventoryService()

    def create_order(self, order: Order) -> None:
        self._save_order(order)
        self._email_sender.send(order.customer_email, "Order confirmed")  # easily missed
        self._inventory_service.reserve(order.items)  # easily missed


# ✅ GOOD — Observer ensures all subscribers are notified automatically
from abc import ABC, abstractmethod
from typing import List


class OrderObserver(ABC):
    @abstractmethod
    def on_order_created(self, order: Order) -> None: ...


class OrderManager:
    def __init__(self) -> None:
        self._observers: List[OrderObserver] = []
        self._orders: List[Order] = []

    def subscribe(self, observer: OrderObserver) -> None:
        self._observers.append(observer)

    def create_order(self, order: Order) -> None:
        self._orders.append(order)
        for observer in self._observers:
            observer.on_order_created(order)


class EmailNotifier(OrderObserver):
    def __init__(self, sender: EmailSender) -> None:
        self._sender = sender

    def on_order_created(self, order: Order) -> None:
        self._sender.send(order.customer_email, "Order confirmed")


class InventoryReserver(OrderObserver):
    def __init__(self, inventory: InventoryService) -> None:
        self._inventory = inventory

    def on_order_created(self, order: Order) -> None:
        self._inventory.reserve(order.items)
```

---

### Command

The Command pattern encapsulates a request as an object, allowing parameterization, queuing, logging, and undo operations. Decouples the invoker from the receiver.

```python
# ✅ GOOD — Commands are first-class objects that can be queued, logged, and undone
from abc import ABC, abstractmethod
from typing import List


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

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


class Light:
    def turn_on(self) -> None:
        self._state = True

    def turn_off(self) -> None:
        self._state = False


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

    def execute(self) -> None:
        self._light.turn_on()

    def undo(self) -> None:
        self._light.turn_off()


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

    def execute(self) -> None:
        self._light.turn_off()

    def undo(self) -> None:
        self._light.turn_on()


class RemoteControl:
    def __init__(self) -> None:
        self._commands: List[Command] = []
        self._history: List[Command] = []

    def press_button(self, command: Command) -> None:
        command.execute()
        self._history.append(command)

    def undo_last(self) -> None:
        if self._history:
            self._history.pop().undo()


# Commands can be queued, stored, and replayed:
# light = Light()
# remote = RemoteControl()
# remote.press_button(LightOnCommand(light))
# remote.undo_last()  # Turns off the light
```

---

### Iterator

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

```python
# ✅ GOOD — Custom iterator for a collection with complex traversal logic
from abc import ABC, abstractmethod
from typing import Any, Iterator


class Container(ABC):
    @abstractmethod
    def create_iterator(self) -> "Iterator": ...


class Iterator(ABC):
    @abstractmethod
    def first(self) -> Any: ...
    @abstractmethod
    def next(self) -> Any: ...
    @abstractmethod
    def is_done(self) -> bool: ...
    @abstractmethod
    def current(self) -> Any: ...


class BinaryIterator(Iterator):
    """Iterator that visits nodes in binary tree order."""

    def __init__(self, tree: list[dict]) -> None:
        self._tree = tree
        self._index = 0

    def first(self) -> Any:
        self._index = 0
        return self.current()

    def next(self) -> Any:
        self._index += 1
        return self.current()

    def is_done(self) -> bool:
        return self._index >= len(self._tree)

    def current(self) -> Any:
        if self.is_done():
            raise StopIteration
        return self._tree[self._index]


class BinarySearchTree(Container):
    def __init__(self) -> None:
        self._nodes: list[dict] = []

    def add(self, value: int) -> None:
        self._nodes.append({"value": value})

    def create_iterator(self) -> Iterator:
        return BinaryIterator(self._nodes)


# Usage:
# tree = BinarySearchTree()
# tree.add(5)
# tree.add(3)
# tree.add(7)
# it = tree.create_iterator()
# while not it.is_done():
#     print(it.next())
```

---

### State

The State pattern allows an object to alter its behavior when its internal state changes. The object appears to change its class. Replaces large conditional blocks that switch behavior based on state.

```python
# ✅ GOOD — Each state is a separate class with explicit transitions
from abc import ABC, abstractmethod


class OrderState(ABC):
    @abstractmethod
    def process_payment(self, amount: float) -> None: ...

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

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

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


class PendingOrderState(OrderState):
    def __init__(self, context: "Order") -> None:
        self._context = context

    def process_payment(self, amount: float) -> None:
        if amount > 0:
            print("Payment processed")
            self._context._state = self._context._paid_state

    def ship(self) -> None:
        print("Cannot ship: order is pending payment")

    def cancel(self) -> None:
        print("Order cancelled")
        self._context._state = self._context._cancelled_state

    def return_order(self) -> None:
        print("Cannot return: order has not shipped")


class PaidOrderState(OrderState):
    def __init__(self, context: "Order") -> None:
        self._context = context

    def process_payment(self, amount: float) -> None:
        print("Payment already processed")

    def ship(self) -> None:
        print("Order shipped")
        self._context._state = self._context._shipped_state

    def cancel(self) -> None:
        print("Cannot cancel: order has been paid. Initiate return.")

    def return_order(self) -> None:
        print("Return initiated")
        self._context._state = self._context._returned_state


class ShippedOrderState(OrderState):
    def __init__(self, context: "Order") -> None:
        self._context = context

    def process_payment(self, amount: float) -> None:
        print("Payment already processed")

    def ship(self) -> None:
        print("Order already shipped")

    def cancel(self) -> None:
        print("Cannot cancel: order has shipped. Initiate return.")

    def return_order(self) -> None:
        print("Return accepted — refund processing")
        self._context._state = self._context._returned_state


class CancelledOrderState(OrderState):
    def __init__(self, context: "Order") -> None:
        self._context = context

    def process_payment(self, amount: float) -> None:
        print("Cannot process payment: order is cancelled")

    def ship(self) -> None:
        print("Cannot ship: order is cancelled")

    def cancel(self) -> None:
        print("Order already cancelled")

    def return_order(self) -> None:
        print("Cannot return: order is cancelled")


class Order:
    def __init__(self) -> None:
        self._pending_state = PendingOrderState(self)
        self._paid_state = PaidOrderState(self)
        self._shipped_state = ShippedOrderState(self)
        self._cancelled_state = CancelledOrderState(self)
        self._returned_state = CancelledOrderState(self)  # simplified
        self._state: OrderState = self._pending_state

    def process_payment(self, amount: float) -> None:
        self._state.process_payment(amount)

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

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

    def return_order(self) -> None:
        self._state.return_order()


# State transitions are explicit and testable:
# order = Order()
# order.process_payment(99.99)  # → PaidOrderS

…(truncated)
