Decorator Design Pattern Tutorial from Scratch (2026)
The Decorator pattern attaches additional responsibilities to an object dynamically at runtime. Decorators provide a flexible alternative to subclassing for extending functionality, allowing behaviors to be layered in a combinatorial fashion without multiplying class count exponentially.
This tutorial demonstrates Decorator through Java IO streams (the classic real-world example), text processing pipelines, and a coffee customization system. You will learn how to build wrapper classes that add behavior before or after delegating to the wrapped object.
Decorator Structure and Coffee Example
The core of the Decorator pattern consists of a Component interface, a ConcreteComponent (the object being decorated), a Decorator abstract class that holds a reference to a Component, and ConcreteDecorators that add behavior. Each decorator wraps another component, creating a chain of responsibilities.
// Component interface
interface Beverage {
String getDescription();
double cost();
}
// Concrete component
class Espresso implements Beverage {
public String getDescription() { return "Espresso"; }
public double cost() { return 1.99; }
}
// Abstract decorator
abstract class CondimentDecorator implements Beverage {
protected Beverage beverage;
public CondimentDecorator(Beverage beverage) {
this.beverage = beverage;
}
public abstract String getDescription();
}
// Concrete decorators
class Mocha extends CondimentDecorator {
public Mocha(Beverage beverage) { super(beverage); }
public String getDescription() {
return beverage.getDescription() + ", Mocha";
}
public double cost() {
return beverage.cost() + 0.20;
}
}
class Whip extends CondimentDecorator {
public Whip(Beverage beverage) { super(beverage); }
public String getDescription() {
return beverage.getDescription() + ", Whip";
}
public double cost() {
return beverage.cost() + 0.10;
}
}
// Usage
Beverage beverage = new Espresso();
beverage = new Mocha(beverage);
beverage = new Mocha(beverage);
beverage = new Whip(beverage);
System.out.println(beverage.getDescription() + " $" + beverage.cost());
// Output: Espresso, Mocha, Mocha, Whip $2.49
Java IO Streams: The Classic Decorator Example
The Java IO library is the most famous real-world example of the Decorator pattern. InputStream is the abstract component, FileInputStream is a concrete component, and FilterInputStream is the abstract decorator. BufferedInputStream, DataInputStream, and PushbackInputStream are concrete decorators that add buffering, data type reading, and pushback capabilities.
// Java IO Decorator example - reading a compressed, encrypted file
import java.io.*;
public class IOExample {
public static void main(String[] args) throws IOException {
// Wrapping chain: layers of decorators
InputStream in = new FileInputStream("data.txt.gz");
in = new BufferedInputStream(in); // add buffering
in = new GZIPInputStream(in); // add decompression
in = new DataInputStream(in); // add primitive reading
// Read data through decorated stream
int value = ((DataInputStream) in).readInt();
System.out.println("Read value: " + value);
in.close();
// Writer example
OutputStream out = new FileOutputStream("output.txt");
out = new BufferedOutputStream(out); // add buffering
out = new GZIPOutputStream(out); // add compression
out = new DataOutputStream(out); // add primitive writing
((DataOutputStream) out).writeInt(42);
out.close();
}
}
Decorator in Python with Function Wrappers
Python's first-class functions and function decorators provide a natural, concise way to implement the Decorator pattern. Python decorators are syntactic sugar for wrapper functions, allowing you to extend the behavior of functions and classes at definition time. This is much less verbose than the class-based approach required in statically typed languages.
from functools import wraps
import time
# Function decorator for logging
def log_execution(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__} with args={args} kwargs={kwargs}")
result = func(*args, **kwargs)
print(f"{func.__name__} returned {result}")
return result
return wrapper
# Function decorator for timing
def timed(func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
elapsed = time.time() - start
print(f"{func.__name__} took {elapsed:.4f}s")
return result
return wrapper
# Stacking decorators
@log_execution
@timed
def compute_factorial(n: int) -> int:
if n <= 1:
return 1
return n * compute_factorial(n - 1)
# The decorators wrap from bottom up:
# log_execution(timed(compute_factorial))
print(compute_factorial(5))
Frequently Asked Questions
What is the difference between Decorator and Inheritance?
Inheritance extends behavior at compile time statically across all instances of a class. Decorator extends behavior at runtime dynamically per object instance. Decorators are more flexible, follow the Open/Closed Principle better, and avoid class explosion when many combinations of behaviors are needed.
How is Decorator different from Proxy?
Both use the wrapper structure, but their intent differs. Decorator adds new behavior to an object, while Proxy controls access to an object. Decorator focuses on enhancing functionality; Proxy focuses on managing access, lifecycle, or cross-cutting concerns.
Can multiple decorators be stacked on one object?
Yes, that is a key advantage of the Decorator pattern. Each decorator wraps the previous one, creating a chain. The order of decorators matters because each one can add behavior before and/or after delegating to the wrapped component.
Are Python function decorators the same as the Decorator pattern?
Python decorators achieve the same goal of runtime extension but are syntactically different. Python's @decorator syntax is syntactic sugar for passing a function through a wrapper. The concept is identical, but Python's implementation is more concise due to first-class functions.
Originally published on Ayodhyyya. Last updated June 1, 2026.