Decorator Design Pattern (original) (raw)

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.

Decorator-Design-Pattern

Decorator Design Pattern

In the above Diagram

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.

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:

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)

#include

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)

/*package whatever //do not write package name here */

#include

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)

#include #include

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)

#include #include

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 #include 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.

Disadvantages

While powerful, this pattern has some downsides: