Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

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

TopicDecorator Design PatternPython @decorator
WrapsObjectsFunctions or methods
GoalAdd runtime responsibilitiesAdd callable behavior automatically
ExampleAdd logging around a notifier objectAdd 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

BenefitTradeoff
Flexible compositionMany wrappers can become hard to inspect
Avoids subclass explosionDebugging order can matter
Keeps base object simpleOveruse can hide control flow

Mini Quiz

  1. What remains stable when decorators are added?

  2. Why is this pattern often better than inheritance for optional features?

  3. What is the most important distinction between this notebook and the Python decorators chapter?

Answer Check
  1. The shared interface

  2. Because features can be combined dynamically

  3. This pattern wraps objects, while Python @decorators wrap 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?

When you want a single fixed behavior for every object forever Decorator is most valuable when optional combinations matter.
When you need to add optional features in different combinations Decorator avoids subclass explosion by composing wrappers dynamically.
When many listeners must react to one event That is an Observer-style problem.
When you need centralized object creation That points toward Factory.

Exercises

  1. Add a CompressionDecorator that annotates the outgoing message.

  2. 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 → Compression

  • B) Compression(Audit(Watermark(base))) applies Watermark first, then Audit, then Compression

  • C) 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.