Skip to content
Structural

Decorator

Attach additional responsibilities to an object dynamically, providing a flexible alternative to subclassing for extending functionality.

Intent

Wrap an object inside another object that adds new behavior before or after delegating to the original, allowing responsibilities to be composed at runtime rather than baked into a rigid class hierarchy.

Problem

You need to add optional behaviors -- such as logging, encryption, compression, or caching -- to individual objects at runtime. Using inheritance to create every possible combination would lead to a class explosion (e.g., LoggingEncryptedCompressedStream), and modifying the original class violates the Open/Closed Principle.

Solution

Define a common interface for both the core component and its decorators. Each decorator implements the interface and holds a reference to a wrapped component. It delegates the core work to the wrapped object and adds its own behavior before or after the delegation. Decorators can be stacked arbitrarily, composing behaviors at runtime.

Participants

  • Component -- the common interface for both concrete objects and decorators
  • ConcreteComponent -- the original object that can be decorated
  • Decorator -- base class or interface that wraps a Component and conforms to its interface
  • ConcreteDecorator -- adds specific behavior before or after delegating to the wrapped Component

Advantages

  • More flexible than static inheritance -- behaviors can be added and removed at runtime
  • Avoids feature-laden classes high up in the hierarchy (class explosion problem)
  • Follows the Single Responsibility Principle -- each decorator handles one concern
  • Decorators can be composed in any order to create custom combinations

Disadvantages

  • Many small objects can make the system harder to understand and debug
  • Removing a specific decorator from the middle of a chain is difficult
  • Decorator ordering can matter, and incorrect ordering leads to subtle bugs
  • The component identity changes -- obj !== decoratedObj, which can break equality checks

Real-World Analogy

Wearing layers of clothing is the Decorator pattern. You start with a base layer (t-shirt), then add a sweater for warmth, then a rain jacket for waterproofing. Each layer adds a responsibility without altering the layers beneath, and you can mix and match depending on conditions.

Use Cases

  • Adding logging, timing, or retry logic to service calls without modifying the service
  • Composing I/O stream behaviors: buffering, compression, encryption
  • Dynamically adding validation rules to form fields in a UI framework
  • Wrapping a data repository with caching or audit-trail functionality
  • Adding authentication or rate-limiting middleware to HTTP handlers

Code Examples

Decorators that add logging and encryption to a DataSource interface, demonstrating how behaviors compose by wrapping one decorator inside another.