Thilan Dissanayaka Software Architecture Apr 26

Decorator Pattern explained simply

When you want to add new functionalities to an object without modifying its structure, the Decorator Pattern comes to the rescue.

The Decorator Pattern lets you dynamically wrap objects with new behavior. It's like layering a cake: each layer (decorator) adds something extra without changing the original cake (object).

What is the Decorator Pattern?

At its core:

  • Attach additional responsibilities to an object dynamically.
  • A flexible alternative to subclassing for extending functionality.
  • You can "decorate" objects multiple times with different decorators.

A Real-Life Analogy

Imagine ordering a coffee:

  • Base coffee = Espresso
  • Add-ons (decorators) = Milk, Sugar, Whipped Cream

Each add-on wraps the base coffee and adds something new without changing the original espresso recipe.

Structure

  • Component: An interface or abstract class defining the operations.
  • ConcreteComponent: The basic object that will be decorated.
  • Decorator: Abstract class that implements the component interface and has a reference to a component.
  • ConcreteDecorators: Add extra behavior.

Simple Java Example

First, define a Coffee interface:

public interface Coffee {
    String getDescription();
    double getCost();
}

Create a basic SimpleCoffee class:

public class SimpleCoffee implements Coffee {
    @Override
    public String getDescription() {
        return "Simple Coffee";
    }

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

Now, create the abstract CoffeeDecorator:

public abstract class CoffeeDecorator implements Coffee {
    protected Coffee decoratedCoffee;

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

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

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

Create concrete decorators:

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

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

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

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

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

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

Using the Decorators

public class MainProgram {
    public static void main(String[] args) {
        Coffee myCoffee = new SimpleCoffee();
        System.out.println(myCoffee.getDescription() + " $" + myCoffee.getCost());

        // Add milk
        myCoffee = new MilkDecorator(myCoffee);
        System.out.println(myCoffee.getDescription() + " $" + myCoffee.getCost());

        // Add sugar
        myCoffee = new SugarDecorator(myCoffee);
        System.out.println(myCoffee.getDescription() + " $" + myCoffee.getCost());
    }
}

Output:

Simple Coffee $5.0
Simple Coffee, Milk $6.5
Simple Coffee, Milk, Sugar $7.0

Why Use the Decorator Pattern?

  • Flexible and Scalable: You can add new features without altering existing code.
  • Avoids Class Explosion: No need to create hundreds of subclasses for every combination of behavior.
  • Single Responsibility Principle: Functionality is divided between classes.

Real-World Use Cases

  • UI Frameworks: Adding borders, scrollbars, and shadows to components.
  • Streams in Java (InputStream, BufferedInputStream, etc.)
  • Logging frameworks: Dynamically add log processors.

Summary

The Decorator Pattern lets you dynamically add behavior to objects without modifying them.
It’s a cleaner, more flexible alternative to subclassing and helps keep your codebase extensible and maintainable.

ALSO READ
 OWASP Top 10 explained - 2021
Mar 03 Application Security

The Open Worldwide Application Security Project (OWASP) is a nonprofit foundation focused on improving the security of software. It provides free, vendor-neutral tools, resources, and standards that....

Exploiting a  Stack Buffer Overflow  on Linux
May 11 Exploit development

Have you ever wondered how attackers gain control over remote servers? How do they just run some exploit and compromise a computer? If we dive into the actual context, there is no magic happening.....

Building a Web3 CLI Tool for the Ballerina Language: From Idea to Reality
Apr 26 WSO2

🚀 Excited to finally share my journey of building a web3 CLI tool for Ballerina! This tool bridges the gap between Ethereum smart contracts and the Ballerina programming language by automatically....

Ballerina connector for Hubspot Schema API
Mar 23 WSO2

Hi all, It's a new article on something cool. Here we are going to see how we can use the Hubspot schema connector with Ballerina. When it comes to building connectors for seamless integration....

Finding the Second Largest Element in an Array
Nov 10 DSA

Hi all, Here I'm back with another algorithmic problem. This is a classical interview question asked almost everywhere. As you may know, I recently did a Software Engineering internship at WSO2.....

Exploiting a Stack Buffer Overflow on Windows
May 17 Exploit development

In a previous tutorial we discusses how we can exploit a buffer overflow vulnerability on a Linux machine. I wen through all theories in depth and explained each step. Now today we are going to....