Skip to content

Commit f258073

Browse files
committed
docs: add design spec for CoR fallback mechanism
1 parent c41ae4d commit f258073

1 file changed

Lines changed: 74 additions & 0 deletions

File tree

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
---
2+
title: Chain of Responsibility — Fallback Mechanism
3+
date: 2026-04-26
4+
status: approved
5+
---
6+
7+
## Summary
8+
9+
Enhance `patterns/behavioral/chain_of_responsibility.py` by integrating a proper fallback mechanism using the Template Method pattern. Add a test suite and real-world documentation.
10+
11+
## Problem
12+
13+
The current `FallbackHandler` must be manually chained at the end of every chain. If a developer forgets it, unhandled requests are silently dropped with no feedback. `handle()` returns `None`, so callers have no way to know whether a request was processed.
14+
15+
## Design
16+
17+
### Pattern Composition: CoR + Template Method
18+
19+
`Handler.handle()` becomes a Template Method that owns the full flow:
20+
21+
```
22+
check_range(request)
23+
↓ not handled
24+
successor exists?
25+
├── yes → successor.handle(request)
26+
└── no → self.handle_fallback(request) ← fires automatically
27+
```
28+
29+
Subclasses fill in `check_range`. The base class calls `handle_fallback` automatically when the chain exhausts — developers never silently drop requests again.
30+
31+
### Handler base class changes
32+
33+
- `handle()` returns `bool` (was `None`)
34+
- New `handle_fallback(request: int) -> bool` — default is a no-op returning `False`. Subclasses can override.
35+
36+
### FallbackHandler — two modes
37+
38+
```python
39+
FallbackHandler(mode="log") # default — prints warning, returns False
40+
FallbackHandler(mode="strict") # raises ValueError — for production systems
41+
```
42+
43+
Constructor arg is preferred over subclasses: simpler, more Pythonic at this scale.
44+
45+
### Documentation
46+
47+
Replace abstract docstring with a **support ticket escalation** real-world analogy:
48+
- L1 Support → L2 Support → L3 Support → fallback fires if none handle it
49+
50+
Add ecosystem reference: Django middleware chain.
51+
52+
### Tests — `tests/behavioral/test_chain_of_responsibility.py`
53+
54+
| Test | What it verifies |
55+
|---|---|
56+
| `test_handler_routes_correctly` | Each handler processes requests in its own range |
57+
| `test_fallback_log_mode` | Unhandled request triggers log fallback, returns `False` |
58+
| `test_fallback_strict_mode` | Unhandled request raises `ValueError` |
59+
| `test_handle_returns_true_on_success` | `handle()` returns `True` when handled |
60+
| `test_handle_returns_false_on_fallback` | `handle()` returns `False` when fallback fires |
61+
| `test_chain_without_fallback_handler` | Chain exhausts gracefully via base no-op (no crash) |
62+
63+
## Files Changed
64+
65+
| File | Change |
66+
|---|---|
67+
| `patterns/behavioral/chain_of_responsibility.py` | Template Method integration, FallbackHandler modes, return types, docstring |
68+
| `tests/behavioral/test_chain_of_responsibility.py` | New file — full pytest suite |
69+
70+
## Non-Goals
71+
72+
- No changes to concrete handlers (ConcreteHandler0/1/2)
73+
- No new dependencies
74+
- Existing doctest in `main()` remains valid

0 commit comments

Comments
 (0)