Decorator Design Pattern¶
Wrap an object with extra features while keeping the same interface.
This notebook covers the design-pattern decorator, not Python’s @decorator syntax from the previous chapter. Here, we wrap objects to add capabilities layer by layer.
Business Framing¶
Suppose you already have a basic notification service. Different business teams now ask for:
logging
priority tagging
SMS fallback
compliance audit messages
You could create many subclasses, but that quickly explodes. Decorator lets you stack features around the same base object.
Visual Intuition¶
Design Pattern vs Python Syntax¶
| Topic | Decorator Design Pattern | Python @decorator |
|---|---|---|
| Wraps | Objects | Functions or methods |
| Goal | Add runtime responsibilities | Add callable behavior automatically |
| Example | Add logging around a notifier object | Add timing around a function |
Worked Example: Layered Notifier¶
Why use decorators instead of subclasses?
Subclasses multiply quickly. Decorators let you compose only the features you actually need, in the order you need them.
When Decorator Shines¶
feature flags around services
logging, caching, retry, and monitoring wrappers
premium features around the same base product
layered request or response handling in APIs
Benefits and Tradeoffs¶
| Benefit | Tradeoff |
|---|---|
| Flexible composition | Many wrappers can become hard to inspect |
| Avoids subclass explosion | Debugging order can matter |
| Keeps base object simple | Overuse can hide control flow |
Mini Quiz¶
What remains stable when decorators are added?
Why is this pattern often better than inheritance for optional features?
What is the most important distinction between this notebook and the Python decorators chapter?
Answer Check
The shared interface
Because features can be combined dynamically
This pattern wraps objects, while Python
@decoratorswrap callables
Practice Prompt¶
Wrap a ReportGenerator object with:
a watermark decorator
a compression decorator
an audit decorator
Design Hint
Start with the simplest base object and then wrap it in the exact order that matches the business workflow.
Interactive Code¶
What to notice
Each wrapper preserves the same send() interface, so new features can be stacked without changing the calling code.
Guided Practice¶
When is Decorator usually better than inheritance?¶
Exercises¶
Add a
CompressionDecoratorthat annotates the outgoing message.Experiment with different wrapping orders and explain how the final output changes.
Exercise hint
Keep the interface stable. Each decorator should accept a wrapped object and return a modified version of the same core result.
Key Takeaway¶
Decorator helps you say: same object, extra powers, no inheritance explosion.
Quick MCQ — Wrapping order¶
A)
Compression(Audit(Watermark(base)))applies Watermark → Audit → CompressionB)
Compression(Audit(Watermark(base)))applies Watermark first, then Audit, then CompressionC)
Compression(Audit(Watermark(base)))applies Compression first, then Audit, then Watermark
Correct: B — inner-most decorator runs first (Watermark), then Audit, then outer Compression.
Suggestion¶
Try swapping decorators locally to observe how the output order changes; this is a quick way to debug wrapped features.