Decorator: wrap one extra behavior
Picture
A sender already works. We want to count successful sends without editing the sender or every caller.
Predict
If the inner sender fails, should a success counter increase?
Walk through the small example
No. Our wrapper increments only after a successful return.
Mental model and complete Python solution
A decorator preserves an interface and layers behavior around a collaborator. Wrappers can be composed, but their order matters: logging outside a retry wrapper sees one logical request, while logging inside it sees each attempt. This example deliberately counts accepted calls, not actual message delivery beyond the sender’s contract.
What the function promises: CountingSender exposes send and increments successes only when the wrapped sender returns normally.
class CountingSender:
def __init__(self, inner):
self.inner = inner
self.successes = 0
def send(self, message):
self.inner.send(message)
self.successes += 1
class LocalSender:
def __init__(self):
self.messages = []
def send(self, message):
if not message:
raise ValueError("Empty message")
self.messages.append(message)
wrapped = CountingSender(LocalSender())
wrapped.send("hello")
assert wrapped.successes == 1
try:
wrapped.send("")
except ValueError:
pass
assert wrapped.successes == 1
Work and storage: O(1) added work per wrapper; a stack of k wrappers adds O(k) call overhead.
When to recognize it: Counting after delegation records successes. Counting before it records attempts. Decide which metric you promised.
Check before applying it: Wrapper order affects observable behavior. Do not assume two decorators commute.
Change one rule
We want to avoid calling the sender on some requests by checking permissions first. Which neighboring idea fits?
See what changes
A protection proxy emphasizes controlling access. Decorator and proxy can look similar in code; distinguish their intent and contract.
Connect
Swap the rule, not the whole feature: A policy chooses behavior; a decorator surrounds existing behavior.
Proxy: control access to a helper: Compare adding behavior with controlling access to the underlying operation.