Decorator Design Pattern

Last Updated : 6 May, 2026

The Decorator Design Pattern is a structural pattern that lets you dynamically add behavior to individual objects without changing other objects of the same class. It uses decorator classes to wrap concrete components, making functionality more flexible and reusable.

  • Wraps existing objects using decorator classes to add new behavior at runtime.
  • Extends functionality without modifying original code, following the open/closed principle.
Decorator-Design-Pattern
Decorator Design Pattern

In the above Diagram

  • This represents Pizza as the base component.
  • Capsicum acts as the first decorator, wrapping the pizza and adding its own cost using getCost().
  • Cheese Burst is another decorator that wraps the previous layer and adds additional cost.
  • The final price is calculated as Pizza + Capsicum + Cheese Burst, demonstrating how the Decorator Pattern adds behavior dynamically.

Real-World Examples

The Decorator Pattern is widely used in software systems to dynamically add new features or behaviors to objects without modifying their original structure.

1. Coffee Shop Application: A basic coffee object is enhanced with add-ons like milk, sugar, or whipped cream. Each add-on acts as a decorator that dynamically adds cost and behavior.

2. Video Streaming Platforms: Videos can be wrapped with decorators for subtitles, audio enhancements, or language options. Multiple features can be added without modifying the original video object.

3. Text Processing Applications: Plain text can be decorated with bold, italic, or underline formatting. Multiple text styles can be combined dynamically using decorators.

4. Video Streaming Platforms: A video object can be enhanced with features like subtitles, language options, or audio enhancements. These features act as decorators, adding functionality without modifying the original video.

5. Java I/O Streams: In Java, a FileInputStream can be wrapped with BufferedInputStream or DataInputStream. Each wrapper adds new functionality without altering the core stream.

Components

The Decorator Pattern consists of key elements that work together to dynamically extend object behavior.

  • Component Interface: Defines common operations for components and decorators.
  • Concrete Component: Core object with basic functionality.
  • Decorator: Abstract wrapper that holds a Component reference and adds behavior.
  • Concrete Decorator: Specific decorators that extend functionality of the component.

Working

The Decorator Pattern works by wrapping objects to add new behavior without modifying their original structure.

  1. Define a component interface that both the core object and decorators implement.
  2. Create a concrete component that implements the interface.
  3. Create decorator classes that also implement the interface and have a reference to a component object.
  4. Decorator classes add or override behavior by delegating to the wrapped object.

Uses

The Decorator Pattern is used when:

  • We want to add responsibilities to objects without subclassing, avoiding the need to create multiple derived classes.
  • We need to combine behaviors flexibly at runtime, allowing features to be added or removed dynamically as needed.
  • We want to extend functionality of a class in a transparent way, without modifying its original code or structure.

Implementation Example

Problem statement

Suppose we are building a coffee shop application where customers can order different types of coffee. Each coffee can have various optional add-ons such as milk, sugar, whipped cream, etc. We want to implement a system where we can dynamically add these add-ons to a coffee order without modifying the coffee classes themselves.

class_diagram_of_decorator_design_pattern
class diagram

Using the Decorator Pattern allows us to add optional features (add-ons) to coffee orders dynamically without altering the core coffee classes. This promotes code flexibility, scalability and maintainability as new add-ons can be easily introduced and combined with different types of coffee orders.

This example shows the practical application of the design pattern using code.

1. Component Interface(Coffee)

  • This is the interface Coffee representing the component.
  • It declares two methods getDescription() and getCost() which must be implemented by concrete components and decorators.
C++
#include <string>

class Coffee {
public:
    virtual std::string getDescription() = 0;
    virtual double getCost() = 0;
};
Java
// Coffee.java
public interface Coffee {
    String getDescription();
    double getCost();
}
Python
from abc import ABC, abstractmethod

class Coffee(ABC):
    @abstractmethod
    def getDescription(self):
        pass

    @abstractmethod
    def getCost(self):
        pass
JavaScript
class Coffee {
    getDescription() {
        throw new Error("Method not implemented.");
    }
    getCost() {
        throw new Error("Method not implemented.");
    }
}

2. ConcreteComponent(PlainCoffee)

  • PlainCoffee is a concrete class implementing the Coffee interface.
  • It provides the description and cost of plain coffee by implementing the getDescription() and getCost() methods.
C++
/*package whatever //do not write package name here */

#include <iostream>

int main() {
    std::cout << "GFG!" << std::endl;
    return 0;
}
Java
/*package whatever //do not write package name here */

import java.io.*;

class GFG {
    public static void main (String[] args) {
        System.out.println("GFG!");
    }
}
Python
/*package whatever //do not write package name here */

# Python3 code
print("GFG!")
JavaScript
/*package whatever //do not write package name here */

// JavaScript code
console.log('GFG!');

3. Decorator(CoffeeDecorator)

  • CoffeeDecorator is an abstract class implementing the Coffee interface.
  • It maintains a reference to the decorated Coffee object.
  • The getDescription() and getCost() methods are implemented to delegate to the decorated coffee object.
C++
#include <string>
#include <iostream>

class Coffee {
public:
    virtual std::string getDescription() = 0;
    virtual double getCost() = 0;
};

class PlainCoffee : public Coffee {
public:
    std::string getDescription() override {
        return "Plain Coffee";
    }

    double getCost() override {
        return 2.0;
    }
};
Java
// PlainCoffee.java
public class PlainCoffee implements Coffee {
    @Override
    public String getDescription() {
        return "Plain Coffee";
    }

    @Override
    public double getCost() {
        return 2.0;
    }
}
Python
from abc import ABC, abstractmethod

class Coffee(ABC):
    @abstractmethod
    def getDescription(self):
        pass

    @abstractmethod
    def getCost(self):
        pass

class PlainCoffee(Coffee):
    def getDescription(self):
        return "Plain Coffee"

    def getCost(self):
        return 2.0
JavaScript
class Coffee {
    getDescription() {
        throw new Error("Method not implemented.");
    }

    getCost() {
        throw new Error("Method not implemented.");
    }
}

class PlainCoffee extends Coffee {
    getDescription() {
        return "Plain Coffee";
    }

    getCost() {
        return 2.0;
    }
}

4. ConcreteDecorators(MilkDecorator,SugarDecorator)

  • MilkDecorator and SugarDecorator are concrete decorators extending CoffeeDecorator.
  • They override getDescription() to add the respective decorator description to the decorated coffee's description.
  • They override getCost() to add the cost of the respective decorator to the decorated coffee's cost.
C++
#include <string>
#include <iostream>

class Coffee {
public:
    virtual std::string getDescription() const = 0;
    virtual double getCost() const = 0;
};

class CoffeeDecorator : public Coffee {
protected:
    Coffee* decoratedCoffee;
public:
    CoffeeDecorator(Coffee* coffee) : decoratedCoffee(coffee) {}
    virtual std::string getDescription() const override {
        return decoratedCoffee->getDescription();
    }
    virtual double getCost() const override {
        return decoratedCoffee->getCost();
    }
};

class MilkDecorator : public CoffeeDecorator {
public:
    MilkDecorator(Coffee* coffee) : CoffeeDecorator(coffee) {}
    std::string getDescription() const override {
        return CoffeeDecorator::getDescription() + ", Milk";
    }
    double getCost() const override {
        return CoffeeDecorator::getCost() + 0.5;
    }
};

class SugarDecorator : public CoffeeDecorator {
public:
    SugarDecorator(Coffee* coffee) : CoffeeDecorator(coffee) {}
    std::string getDescription() const override {
        return CoffeeDecorator::getDescription() + ", Sugar";
    }
    double getCost() const override {
        return CoffeeDecorator::getCost() + 0.2;
    }
};
Java
public interface Coffee {
    String getDescription();
    double getCost();
}

public abstract class CoffeeDecorator implements Coffee {
    protected Coffee decoratedCoffee;
    public CoffeeDecorator(Coffee decoratedCoffee) {
        this.decoratedCoffee = decoratedCoffee;
    }
    public String getDescription() {
        return decoratedCoffee.getDescription();
    }
    public double getCost() {
        return decoratedCoffee.getCost();
    }
}

public class MilkDecorator extends CoffeeDecorator {
    public MilkDecorator(Coffee decoratedCoffee) {
        super(decoratedCoffee);
    }
    @Override
    public String getDescription() {
        return decoratedCoffee.getDescription() + ", Milk";
    }
    @Override
    public double getCost() {
        return decoratedCoffee.getCost() + 0.5;
    }
}

public class SugarDecorator extends CoffeeDecorator {
    public SugarDecorator(Coffee decoratedCoffee) {
        super(decoratedCoffee);
    }
    @Override
    public String getDescription() {
        return decoratedCoffee.getDescription() + ", Sugar";
    }
    @Override
    public double getCost() {
        return decoratedCoffee.getCost() + 0.2;
    }
}
Python
class Coffee:
    def get_description(self):
        raise NotImplementedError("Method not implemented.")

    def get_cost(self):
        raise NotImplementedError("Method not implemented.")

class CoffeeDecorator:
    def __init__(self, decorated_coffee):
        self.decorated_coffee = decorated_coffee

    def get_description(self):
        return self.decorated_coffee.get_description()

    def get_cost(self):
        return self.decorated_coffee.get_cost()

class MilkDecorator(CoffeeDecorator):
    def get_description(self):
        return self.decorated_coffee.get_description() + ", Milk"

    def get_cost(self):
        return self.decorated_coffee.get_cost() + 0.5

class SugarDecorator(CoffeeDecorator):
    def get_description(self):
        return self.decorated_coffee.get_description() + ", Sugar"

    def get_cost(self):
        return self.decorated_coffee.get_cost() + 0.2
JavaScript
class Coffee {
    getDescription() {
        throw new Error('Method not implemented.');
    }
    getCost() {
        throw new Error('Method not implemented.');
    }
}

class CoffeeDecorator {
    constructor(decoratedCoffee) {
        this.decoratedCoffee = decoratedCoffee;
    }
    getDescription() {
        return this.decoratedCoffee.getDescription();
    }
    getCost() {
        return this.decoratedCoffee.getCost();
    }
}

class MilkDecorator extends CoffeeDecorator {
    getDescription() {
        return this.decoratedCoffee.getDescription() + ", Milk";
    }
    getCost() {
        return this.decoratedCoffee.getCost() + 0.5;
    }
}

class SugarDecorator extends CoffeeDecorator {
    getDescription() {
        return this.decoratedCoffee.getDescription() + ", Sugar";
    }
    getCost() {
        return this.decoratedCoffee.getCost() + 0.2;
    }
}

Complete Code of above example:

C++
#include <iostream>
#include <string>
using namespace std;

class Coffee {
public:
    virtual string getDescription() = 0;
    virtual double getCost() = 0;
};

class PlainCoffee : public Coffee {
public:
    string getDescription() override {
        return "Plain Coffee";
    }

    double getCost() override {
        return 2.0;
    }
};

class CoffeeDecorator : public Coffee {
protected:
    Coffee* decoratedCoffee;

public:
    CoffeeDecorator(Coffee* decoratedCoffee) : decoratedCoffee(decoratedCoffee) {}

    string getDescription() override {
        return decoratedCoffee->getDescription();
    }

    double getCost() override {
        return decoratedCoffee->getCost();
    }
};

class MilkDecorator : public CoffeeDecorator {
public:
    MilkDecorator(Coffee* decoratedCoffee) : CoffeeDecorator(decoratedCoffee) {}

    string getDescription() override {
        return decoratedCoffee->getDescription() + ", Milk";
    }

    double getCost() override {
        return decoratedCoffee->getCost() + 0.5;
    }
};

class SugarDecorator : public CoffeeDecorator {
public:
    SugarDecorator(Coffee* decoratedCoffee) : CoffeeDecorator(decoratedCoffee) {}

    string getDescription() override {
        return decoratedCoffee->getDescription() + ", Sugar";
    }

    double getCost() override {
        return decoratedCoffee->getCost() + 0.2;
    }
};

int main() {
    // Plain Coffee
    Coffee* coffee = new PlainCoffee();
    cout << "Description: " << coffee->getDescription() << endl;
    cout << "Cost: $" << coffee->getCost() << endl;

    // Coffee with Milk
    Coffee* milkCoffee = new MilkDecorator(new PlainCoffee());
    cout << "\nDescription: " << milkCoffee->getDescription() << endl;
    cout << "Cost: $" << milkCoffee->getCost() << endl;

    // Coffee with Sugar and Milk
    Coffee* sugarMilkCoffee = new SugarDecorator(new MilkDecorator(new PlainCoffee()));
    cout << "\nDescription: " << sugarMilkCoffee->getDescription() << endl;
    cout << "Cost: $" << sugarMilkCoffee->getCost() << endl;

    delete coffee;
    delete milkCoffee;
    delete sugarMilkCoffee;
    return 0;
}
Java
interface Coffee {
    String getDescription();
    double getCost();
}

class PlainCoffee implements Coffee {
    @Override
    public String getDescription() {
        return "Plain Coffee";
    }

    @Override
    public double getCost() {
        return 2.0;
    }
}

abstract class CoffeeDecorator implements Coffee {
    protected Coffee decoratedCoffee;

    public CoffeeDecorator(Coffee decoratedCoffee) {
        this.decoratedCoffee = decoratedCoffee;
    }

    @Override
    public String getDescription() {
        return decoratedCoffee.getDescription();
    }

    @Override
    public double getCost() {
        return decoratedCoffee.getCost();
    }
}

class MilkDecorator extends CoffeeDecorator {
    public MilkDecorator(Coffee decoratedCoffee) {
        super(decoratedCoffee);
    }

    @Override
    public String getDescription() {
        return decoratedCoffee.getDescription() + ", Milk";
    }

    @Override
    public double getCost() {
        return decoratedCoffee.getCost() + 0.5;
    }
}

class SugarDecorator extends CoffeeDecorator {
    public SugarDecorator(Coffee decoratedCoffee) {
        super(decoratedCoffee);
    }

    @Override
    public String getDescription() {
        return decoratedCoffee.getDescription() + ", Sugar";
    }

    @Override
    public double getCost() {
        return decoratedCoffee.getCost() + 0.2;
    }
}

public class Main {
    public static void main(String[] args) {
        // Plain Coffee
        Coffee coffee = new PlainCoffee();
        System.out.println("Description: " + coffee.getDescription());
        System.out.println("Cost: $" + coffee.getCost());

        // Coffee with Milk
        Coffee milkCoffee = new MilkDecorator(new PlainCoffee());
        System.out.println("\nDescription: " + milkCoffee.getDescription());
        System.out.println("Cost: $" + milkCoffee.getCost());

        // Coffee with Sugar and Milk
        Coffee sugarMilkCoffee = new SugarDecorator(new MilkDecorator(new PlainCoffee()));
        System.out.println("\nDescription: " + sugarMilkCoffee.getDescription());
        System.out.println("Cost: $" + sugarMilkCoffee.getCost());
    }
}
Python
from abc import ABC, abstractmethod

class Coffee(ABC):
    @abstractmethod
    def get_description(self):
        pass

    @abstractmethod
    def get_cost(self):
        pass

class PlainCoffee(Coffee):
    def get_description(self):
        return "Plain Coffee"

    def get_cost(self):
        return 2.0

class CoffeeDecorator(Coffee):
    def __init__(self, decorated_coffee):
        self.decorated_coffee = decorated_coffee

    def get_description(self):
        return self.decorated_coffee.get_description()

    def get_cost(self):
        return self.decorated_coffee.get_cost()

class MilkDecorator(CoffeeDecorator):
    def get_description(self):
        return self.decorated_coffee.get_description() + ", Milk"

    def get_cost(self):
        return self.decorated_coffee.get_cost() + 0.5

class SugarDecorator(CoffeeDecorator):
    def get_description(self):
        return self.decorated_coffee.get_description() + ", Sugar"

    def get_cost(self):
        return self.decorated_coffee.get_cost() + 0.2

def main():
    # Plain Coffee
    coffee = PlainCoffee()
    print(f"Description: {coffee.get_description()}")
    print(f"Cost: ${coffee.get_cost()}")

    # Coffee with Milk
    milk_coffee = MilkDecorator(PlainCoffee())
    print(f"\nDescription: {milk_coffee.get_description()}")
    print(f"Cost: ${milk_coffee.get_cost()}")

    # Coffee with Sugar and Milk
    sugar_milk_coffee = SugarDecorator(MilkDecorator(PlainCoffee()))
    print(f"\nDescription: {sugar_milk_coffee.get_description()}")
    print(f"Cost: ${sugar_milk_coffee.get_cost()}")

if __name__ == "__main__":
    main()
JavaScript
class Coffee {
    getDescription() {
        throw new Error('Method not implemented.');
    }

    getCost() {
        throw new Error('Method not implemented.');
    }
}

class PlainCoffee extends Coffee {
    getDescription() {
        return 'Plain Coffee';
    }

    getCost() {
        return 2.0;
    }
}

class CoffeeDecorator extends Coffee {
    constructor(decoratedCoffee) {
        super();
        this.decoratedCoffee = decoratedCoffee;
    }

    getDescription() {
        return this.decoratedCoffee.getDescription();
    }

    getCost() {
        return this.decoratedCoffee.getCost();
    }
}

class MilkDecorator extends CoffeeDecorator {
    getDescription() {
        return this.decoratedCoffee.getDescription() + ', Milk';
    }

    getCost() {
        return this.decoratedCoffee.getCost() + 0.5;
    }
}

class SugarDecorator extends CoffeeDecorator {
    getDescription() {
        return this.decoratedCoffee.getDescription() + ', Sugar';
    }

    getCost() {
        return this.decoratedCoffee.getCost() + 0.2;
    }
}

function main() {
    // Plain Coffee
    let coffee = new PlainCoffee();
    console.log('Description: ' + coffee.getDescription());
    console.log('Cost: $' + coffee.getCost());

    // Coffee with Milk
    let milkCoffee = new MilkDecorator(new PlainCoffee());
    console.log('\nDescription: ' + milkCoffee.getDescription());
    console.log('Cost: $' + milkCoffee.getCost());

    // Coffee with Sugar and Milk
    let sugarMilkCoffee = new SugarDecorator(new MilkDecorator(new PlainCoffee()));
    console.log('\nDescription: ' + sugarMilkCoffee.getDescription());
    console.log('Cost: $' + sugarMilkCoffee.getCost());
}

main();

Output
Description: Plain Coffee
Cost: $2

Description: Plain Coffee, Milk
Cost: $2.5

Description: Plain Coffee, Milk, Sugar
Cost: $2.7

Advantages

This pattern is useful when you want to add responsibilities to objects dynamically at runtime.

  • Improves flexibility by allowing behavior to be added or removed at runtime.
  • Promotes Single Responsibility Principle, as functionality can be divided among classes.
  • Avoids the use of large subclasses that combine multiple behaviors.

Disadvantages

While powerful, this pattern has some downsides:

  • Can make the system more complex due to many small decorator classes.
  • Debugging and understanding the flow of decorated objects may be harder.
  • Overuse can lead to code that is difficult to maintain.
Comment

Explore