Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ stdlib_sightings: [functools.wraps, contextlib.contextmanager]
Verdicts: `pythonic` = use it as shown; `use-with-care` = valid but has sharp edges
(caveats say which); `prefer-alternative` = the classic form exists for study in
docs/fundamentals.md, `pattern/` exports the alternative to write instead
(e.g. Singleton → module global, Visitor → singledispatch). See `docs/verdicts.md`.
(e.g. Singleton → module global, Visitor → singledispatch).

## Workflow

Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ stdlib_sightings: [functools.wraps, contextlib.contextmanager]
Verdicts: `pythonic` = use it as shown; `use-with-care` = valid but has sharp edges
(caveats say which); `prefer-alternative` = the classic form exists for study in
docs/fundamentals.md, `pattern/` exports the alternative to write instead
(e.g. Singleton → module global, Visitor → singledispatch). See `docs/verdicts.md`.
(e.g. Singleton → module global, Visitor → singledispatch).

## Workflow

Expand Down
24 changes: 12 additions & 12 deletions docs/contributing.md → CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,17 @@ takes only reviewed milestone merges. CI (3.11/3.12/3.13) must pass.
## Adding a pattern unit

1. Scaffold: `/new-pattern <group>/<slug> "Name"` (Claude Code) or copy an
existing unit's shape.
2. Fill the frontmatter — every key; `id` must equal `<group>/<slug>`; pick
the verdict per [verdicts.md](verdicts.md). The catalog loader validates
this in CI and fails loudly.
3. Build the module (see [how-to-read-this-repo.md](how-to-read-this-repo.md)
for what each part is for): `pattern/` (the importable code), the three
`docs/` files, at least one `examples/<project>/` mini-project that
genuinely imports `pattern/`, and behavioral tests for both.
4. `make check` — ruff, mypy --strict, pytest must all pass. The loader
rejects a unit missing any part of the template, and a catalog test
rejects an example that never imports its own pattern package.
existing unit's shape (the template is in [CLAUDE.md](CLAUDE.md)).
2. Fill the frontmatter — every key; `id` must equal `<group>/<slug>`; pick the
verdict (`pythonic` | `use-with-care` | `prefer-alternative`, defined in
[CLAUDE.md](CLAUDE.md)). The catalog loader validates this in CI and fails loudly.
3. Build the module: `pattern/` (the importable code), the three `docs/` files,
at least one `examples/<project>/` mini-project that genuinely imports
`pattern/` (entry point `main.py`, never `__main__.py`), and behavioral
tests for both.
4. `make check` — ruff, mypy --strict, pytest must all pass. The loader rejects
a unit missing any part of the template, and a catalog test rejects an
example that never imports its own pattern package.
5. `make readme` — regenerate the catalog table (CI rejects a stale one).

## Quality bar
Expand All @@ -29,4 +29,4 @@ takes only reviewed milestone merges. CI (3.11/3.12/3.13) must pass.
are severity-ordered; machines own style, humans argue design.
- Mini-projects use realistic domains, no Foo/Bar.
- Prose: one page, problem-first, no UML, no history lessons. The classic
(GoF) form lives in `docs/fundamentals.md` as an annotated listing.
(GoF) form lives in each unit's `docs/fundamentals.md` as an annotated listing.
132 changes: 48 additions & 84 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,104 +1,68 @@
# Python Design Patterns

Look up any design pattern and see what a fluent Python developer would
*actually* write — the classic GoF form contrasted with the Python form,
importable reference code, and a mini-project that puts it to work — with an
honest verdict when the right answer is "don't". All 23 Gang of Four patterns
plus Python-native and modern ones, every unit typed, tested, and runnable.
Every design pattern as a fluent Python developer would actually write it —
the classic GoF form contrasted with the Python form, importable code, a
runnable mini-project, and an honest verdict when the right answer is "don't".

## Use it
## MCP

```bash
# add to Claude Code
claude mcp add design-patterns -- uv run --directory <this-repo> python-design-patterns-mcp

Each pattern is a self-contained module:
# stdio server, for any MCP client
uv run python-design-patterns-mcp

# streamable HTTP on /mcp
uv run python-design-patterns-mcp --http --host 127.0.0.1 --port 8734
```
patterns/structural/decorator/
├── README.md # the problem, the verdict, the map
├── pattern/ # the pattern as importable, typed code
├── docs/ # fundamentals · implementation · cited external examples
├── examples/ # runnable mini-projects that use pattern/
└── tests/ # behavioral tests for both
```

## Use it

```python
from patterns.structural.decorator import retry, logged
```

Run any mini-project: `uv run python -m patterns.structural.decorator.examples.resilient_client.main`

Give it to your agents (MCP server with search, runnable examples, and
pattern recommendations):

```bash
claude mcp add design-patterns -- uv run --directory <this-repo> python-design-patterns-mcp
```

## Catalog

Based on [python-patterns.guide](https://python-patterns.guide/).

<!-- catalog:begin (generated: make readme) -->

### Principles

| Pattern | Verdict | Problem it solves |
|---|---|---|
| [Composition Over Inheritance](patterns/principle/composition_over_inheritance/) | ✅ pythonic | Vary independent behaviors without one subclass per combination of them. |

### Python-native

| Pattern | Verdict | Problem it solves |
|---|---|---|
| [Global Object](patterns/python/global_object/) | ⚠️ use with care | Give a whole program shared access to a constant or a pre-built object by assigning it at module level. |
| [Prebound Method](patterns/python/prebound_method/) | ✅ pythonic | Offer module-level functions that share state, by binding the methods of one hidden instance to module globals. |
| [Sentinel Object](patterns/python/sentinel_object/) | ✅ pythonic | Mark 'no value here' unambiguously when None itself is a legitimate value. |

### Creational (GoF)

| Pattern | Verdict | Problem it solves |
|---|---|---|
| [Abstract Factory](patterns/creational/abstract_factory/) | 🔄 prefer alternative | Let code build families of related objects without naming their concrete classes. |
| [Builder](patterns/creational/builder/) | ⚠️ use with care | Assemble a complex object step by step, so the assembly process is reusable and readable. |
| [Factory Method](patterns/creational/factory_method/) | 🔄 prefer alternative | Let a class defer which helper object it constructs, so subclasses or callers can substitute another. |
| [Prototype](patterns/creational/prototype/) | 🔄 prefer alternative | Create new objects by copying a pre-configured exemplar instead of constructing from scratch. |
| [Singleton](patterns/creational/singleton/) | 🔄 prefer alternative | Guarantee a class has exactly one instance and give the whole program access to it. |

### Structural (GoF)

| Pattern | Verdict | Problem it solves |
|---|---|---|
| [Adapter](patterns/structural/adapter/) | ✅ pythonic | Make an existing class usable through the interface your code expects, without editing either side. |
| [Bridge](patterns/structural/bridge/) | 🔄 prefer alternative | Let an abstraction and its implementation vary independently, instead of multiplying subclasses across both axes. |
| [Composite](patterns/structural/composite/) | ✅ pythonic | Let callers treat a single object and a whole tree of objects through one interface. |
| [Decorator](patterns/structural/decorator/) | ✅ pythonic | Add behavior around an object or callable without editing it or subclassing it. |
| [Facade](patterns/structural/facade/) | ✅ pythonic | Give a complicated subsystem one simple entry point for the common case. |
| [Flyweight](patterns/structural/flyweight/) | ⚠️ use with care | Support huge numbers of fine-grained objects by sharing immutable instances instead of duplicating them. |
| [Proxy](patterns/structural/proxy/) | ⚠️ use with care | Stand in for another object to control access to it — deferring, guarding, or instrumenting the real thing. |

### Behavioral (GoF)

| Pattern | Verdict | Problem it solves |
|---|---|---|
| [Chain of Responsibility](patterns/behavioral/chain_of_responsibility/) | 🔄 prefer alternative | Pass a request along a line of handlers until one of them takes it. |
| [Command](patterns/behavioral/command/) | ⚠️ use with care | Package a request as an object so it can be queued, logged, undone, or executed later by code that doesn't know its details. |
| [Interpreter](patterns/behavioral/interpreter/) | 🔄 prefer alternative | Represent a small language's grammar as data and evaluate sentences in it. |
| [Iterator](patterns/behavioral/iterator/) | ✅ pythonic | Traverse a container's elements without exposing how the container stores them. |
| [Mediator](patterns/behavioral/mediator/) | ⚠️ use with care | Stop a web of objects from referencing each other by routing their interactions through one coordinator. |
| [Memento](patterns/behavioral/memento/) | ⚠️ use with care | Capture an object's state so it can be restored later, without exposing its internals. |
| [Observer](patterns/behavioral/observer/) | ✅ pythonic | Notify interested parties when something changes, without the subject knowing who they are. |
| [State](patterns/behavioral/state/) | ⚠️ use with care | Change an object's behavior when its internal state changes, without an if-forest over a mode flag. |
| [Strategy](patterns/behavioral/strategy/) | 🔄 prefer alternative | Make an algorithm interchangeable at runtime without the caller knowing which variant it got. |
| [Template Method](patterns/behavioral/template_method/) | 🔄 prefer alternative | Fix an algorithm's skeleton while letting callers vary individual steps. |
| [Visitor](patterns/behavioral/visitor/) | 🔄 prefer alternative | Run a new operation over every node of an object structure without adding a method to every node class. |

### Modern Python

| Pattern | Verdict | Problem it solves |
|---|---|---|
| [Async Producer/Consumer](patterns/modern/async_producer_consumer/) | ⚠️ use with care | Decouple work generation from work processing under asyncio, with bounded memory and clean shutdown. |
| [Context Manager](patterns/modern/context_manager/) | ✅ pythonic | Guarantee acquire/release pairing around a block of code, even when it raises. |
| [Dependency Injection](patterns/modern/dependency_injection/) | ✅ pythonic | Hand an object its collaborators instead of letting it construct them, so they can be swapped — above all in tests. |
| [Registry](patterns/modern/registry/) | ✅ pythonic | Let implementations announce themselves by name, so dispatch is a lookup instead of an if/elif ladder. |
| [Repository](patterns/modern/repository/) | ⚠️ use with care | Keep domain logic ignorant of how objects are stored, behind a collection-like interface. |
| Pattern | Group | Verdict | Problem it solves |
|---|---|---|---|
| [Composition Over Inheritance](patterns/principle/composition_over_inheritance/) | Principles | ✅ pythonic | Vary independent behaviors without one subclass per combination of them. |
| [Global Object](patterns/python/global_object/) | Python-native | ⚠️ use with care | Give a whole program shared access to a constant or a pre-built object by assigning it at module level. |
| [Prebound Method](patterns/python/prebound_method/) | Python-native | ✅ pythonic | Offer module-level functions that share state, by binding the methods of one hidden instance to module globals. |
| [Sentinel Object](patterns/python/sentinel_object/) | Python-native | ✅ pythonic | Mark 'no value here' unambiguously when None itself is a legitimate value. |
| [Abstract Factory](patterns/creational/abstract_factory/) | Creational (GoF) | 🔄 prefer alternative | Let code build families of related objects without naming their concrete classes. |
| [Builder](patterns/creational/builder/) | Creational (GoF) | ⚠️ use with care | Assemble a complex object step by step, so the assembly process is reusable and readable. |
| [Factory Method](patterns/creational/factory_method/) | Creational (GoF) | 🔄 prefer alternative | Let a class defer which helper object it constructs, so subclasses or callers can substitute another. |
| [Prototype](patterns/creational/prototype/) | Creational (GoF) | 🔄 prefer alternative | Create new objects by copying a pre-configured exemplar instead of constructing from scratch. |
| [Singleton](patterns/creational/singleton/) | Creational (GoF) | 🔄 prefer alternative | Guarantee a class has exactly one instance and give the whole program access to it. |
| [Adapter](patterns/structural/adapter/) | Structural (GoF) | ✅ pythonic | Make an existing class usable through the interface your code expects, without editing either side. |
| [Bridge](patterns/structural/bridge/) | Structural (GoF) | 🔄 prefer alternative | Let an abstraction and its implementation vary independently, instead of multiplying subclasses across both axes. |
| [Composite](patterns/structural/composite/) | Structural (GoF) | ✅ pythonic | Let callers treat a single object and a whole tree of objects through one interface. |
| [Decorator](patterns/structural/decorator/) | Structural (GoF) | ✅ pythonic | Add behavior around an object or callable without editing it or subclassing it. |
| [Facade](patterns/structural/facade/) | Structural (GoF) | ✅ pythonic | Give a complicated subsystem one simple entry point for the common case. |
| [Flyweight](patterns/structural/flyweight/) | Structural (GoF) | ⚠️ use with care | Support huge numbers of fine-grained objects by sharing immutable instances instead of duplicating them. |
| [Proxy](patterns/structural/proxy/) | Structural (GoF) | ⚠️ use with care | Stand in for another object to control access to it — deferring, guarding, or instrumenting the real thing. |
| [Chain of Responsibility](patterns/behavioral/chain_of_responsibility/) | Behavioral (GoF) | 🔄 prefer alternative | Pass a request along a line of handlers until one of them takes it. |
| [Command](patterns/behavioral/command/) | Behavioral (GoF) | ⚠️ use with care | Package a request as an object so it can be queued, logged, undone, or executed later by code that doesn't know its details. |
| [Interpreter](patterns/behavioral/interpreter/) | Behavioral (GoF) | 🔄 prefer alternative | Represent a small language's grammar as data and evaluate sentences in it. |
| [Iterator](patterns/behavioral/iterator/) | Behavioral (GoF) | ✅ pythonic | Traverse a container's elements without exposing how the container stores them. |
| [Mediator](patterns/behavioral/mediator/) | Behavioral (GoF) | ⚠️ use with care | Stop a web of objects from referencing each other by routing their interactions through one coordinator. |
| [Memento](patterns/behavioral/memento/) | Behavioral (GoF) | ⚠️ use with care | Capture an object's state so it can be restored later, without exposing its internals. |
| [Observer](patterns/behavioral/observer/) | Behavioral (GoF) | ✅ pythonic | Notify interested parties when something changes, without the subject knowing who they are. |
| [State](patterns/behavioral/state/) | Behavioral (GoF) | ⚠️ use with care | Change an object's behavior when its internal state changes, without an if-forest over a mode flag. |
| [Strategy](patterns/behavioral/strategy/) | Behavioral (GoF) | 🔄 prefer alternative | Make an algorithm interchangeable at runtime without the caller knowing which variant it got. |
| [Template Method](patterns/behavioral/template_method/) | Behavioral (GoF) | 🔄 prefer alternative | Fix an algorithm's skeleton while letting callers vary individual steps. |
| [Visitor](patterns/behavioral/visitor/) | Behavioral (GoF) | 🔄 prefer alternative | Run a new operation over every node of an object structure without adding a method to every node class. |
| [Async Producer/Consumer](patterns/modern/async_producer_consumer/) | Modern Python | ⚠️ use with care | Decouple work generation from work processing under asyncio, with bounded memory and clean shutdown. |
| [Context Manager](patterns/modern/context_manager/) | Modern Python | ✅ pythonic | Guarantee acquire/release pairing around a block of code, even when it raises. |
| [Dependency Injection](patterns/modern/dependency_injection/) | Modern Python | ✅ pythonic | Hand an object its collaborators instead of letting it construct them, so they can be swapped — above all in tests. |
| [Registry](patterns/modern/registry/) | Modern Python | ✅ pythonic | Let implementations announce themselves by name, so dispatch is a lookup instead of an if/elif ladder. |
| [Repository](patterns/modern/repository/) | Modern Python | ⚠️ use with care | Keep domain logic ignorant of how objects are stored, behind a collection-like interface. |
<!-- catalog:end -->

More in [docs/](docs/index.md) — verdict definitions, MCP reference, contributing.
39 changes: 0 additions & 39 deletions docs/how-to-read-this-repo.md

This file was deleted.

6 changes: 0 additions & 6 deletions docs/index.md

This file was deleted.

Loading
Loading