Skip to main content
Articles/design-patterns/the-decorator-pattern-enhancing-objects-on-the-fly
Scenario Article

The Decorator Pattern: Enhancing Objects on the Fly 🎁

The Class Explosion Problem

Abstract Algorithmsβ€’Jul 19, 2026
XLinkedInReddit
πŸ’‘ Executive Summary

The Decorator Pattern allows you to add new behaviors or responsibilities to individual objects dynamically, wrapping them in "decorator" objects.

🎯 System Scenario & Requirements

System Goal

The Decorator Pattern allows you to add new behaviors or responsibilities to individual objects dynamically, wrapping them in "decorator" objects.

Actors & Components

  • Application or API tier
  • Core service under design
  • Operator or SRE observer

Functional Requirements

  • TLDR: The Decorator Pattern allows you to add new behaviors or responsibilities to individual objects dynamically, wrapping them in "decorator" objects.
  • It's like adding layers to a gift 🎁, enhancing its features without changing the original item, crucial for flexible and extensible software design.
  • Imagine you're building a system for a coffee shop.

Constraints & Assumptions

  • Use the Decorator Pattern when you need to add responsibilities to individual objects dynamically and transparently, without affecting other objects.
  • It's excellent for adhering to the Open/Closed Principle (open for extension, closed for modification).

⚑ Execution Stages & Decision Trace

1

Stage 1 β€” The Decorator's Elegant Solution

Action / Decision:

The Decorator Pattern offers a flexible alternative to subclassing for extending functionality.

Component Behavior:

It involves creating a set of "decorator" classes that wrap the original object, adding new responsibilities transparently.

Observable Output: Both the original component and the decorators share a common interface, allowing them to be used interchangeably.
Read Full Stage in Deep Dive ↓
πŸ“–

Deep Dive

Detailed Architectural Analysis

Scenario context

The Decorator Pattern allows you to add new behaviors or responsibilities to individual objects dynamically, wrapping them in "decorator" objects. It's like adding layers to a gift 🎁, enhancing its features without changing the original item, crucial for flexible and extensible software design. Imagine you're building a system for a coffee shop. You start with a Coffee class. Now, customers want Milk, Sugar,…

This walkthrough focuses on The Class Explosion Problem.

Before making a design choice, clarify:

  • Imagine you're building a system for a coffee shop.
  • You start with a Coffee class.
  • Now, customers want Milk , Sugar , Whip , or even Caramel in their coffee.

Concepts in play: low-level-design-systems, design-patterns-gof, jvm-internals-and-garbage-collection, design-principles-solid

Stage 1 β€” The Decorator's Elegant Solution

  • The Decorator Pattern offers a flexible alternative to subclassing for extending functionality.
  • It involves creating a set of "decorator" classes that wrap the original object, adding new responsibilities transparently.
  • Both the original component and the decorators share a common interface, allowing them to be used interchangeably.
  • Each decorator adds its own behavior (e.g., modifies cost or description) before or after delegating the call to the wrapped component.

Observable outcome: Each decorator adds its own behavior (e.g., modifies cost or description) before or after delegating the call to the wrapped component.

Stage 2 β€” Crafting a Custom Coffee with Decorators

  • Here's how the Decorator Pattern works in practice, building a customizable coffee:
// β˜• The Component Interface
interface Coffee {
    String getDescription();
    double getCost();
}

// β˜• Concrete Component: The base item
class SimpleCoffee implements Coffee {
    public String getDescription() { return "Simple Coffee"; }
    public double getCost() { return 2.0; }
}

// 🎁 Abstract Decorator: Holds a reference to the wrapped object
abstract class CoffeeDecorator implements Coffee {
    protected Coffee decoratedCoffee;
    public CoffeeDecorator(Coffee coffee) {
        this.decoratedCoffee = coffee;
    }
    // Delegates to the decorated object by default
    public String getDescription() { return decoratedCoffee.getDescription(); }
    public double getCost() { return decoratedCoffee.getCost(); }
}

// πŸ₯› Concrete Decorator: Adds Milk
class MilkDecorator extends CoffeeDecorator {
    public MilkDecorator(Coffee coffee) { super(coffee); }
    public String getDescription() { return decoratedCoffee.getDescription() + ", Milk"; }
    public double getCost() { return decoratedCoffee.getCost() + 0.5; }
}

// 🍬 Concrete Decorator: Adds Sugar
class SugarDecorator extends CoffeeDecorator {
    public SugarDecorator(Coffee coffee) { super(coffee); }
    public String getDescription() { return decoratedCoffee.getDescription() + ", Sugar"; }
    public double getCost() { return decoratedCoffee.getCost() + 0.2; }
}

// πŸš€ Usage Example
public class CoffeeShop {
    public static void main(String[] args) {
        Coffee myCoffee = new SimpleCoffee(); // Start with a simple coffee
        System.out.println(myCoffee.getDescription() + ": $" + myCoffee.getCost());
        // Output: Simple Coffee: $2.0

        myCoffee = new MilkDecorator(myCoffee); // Decorate with Milk
        System.out.println(myCoffee.getDescription() + ": $" + myCoffee.getCost());
        // Output: Simple Coffee, Milk: $2.5

        myCoffee = new SugarDecorator(myCoffee); // Decorate with Sugar
        System.out.println(myCoffee.getDescription() + ": $" + myCoffee.getCost());
        // Output: Simple Coffee, Milk, Sugar: $2.7
    }
}

Observable outcome: Here's how the Decorator Pattern works in practice, building a customizable coffee:

End-to-end outcome

Imagine you're building a system for a coffee shop.

Alternatives considered

The source focuses on one primary design. A second architecture is not compared, so no alternative is invented here.

Failure and degraded modes

When to Decorate (and When Not To)

  • Detection: Use the Decorator Pattern when you need to add responsibilities to individual objects dynamically and transparently, without affecting other objects.
  • User or operator impact: Use the Decorator Pattern when you need to add responsibilities to individual objects dynamically and transparently, without affecting other objects.
βœ…

Key learnings

  • The Decorator Pattern offers a flexible alternative to subclassing for extending functionality.
  • Here's how the Decorator Pattern works in practice, building a customizable coffee:

Share this case study

Discuss architecture trade-offs and lessons with your team.

Share:XLinkedInReddit