Decorator helps when behavior should be added flexibly around an object without subclassing every combination.
Attach additional responsibilities to an object dynamically while keeping the same interface visible to clients.
What the pattern is trying to do
Attach additional responsibilities to an object dynamically while keeping the same interface visible to clients.
What force creates the need
Optional or combinable behavior keeps growing, and inheritance would require too many subclasses to represent every variation.
How the pattern responds
Wrap the object with decorator layers that implement the same interface and add behavior before or after delegation.
How the moving parts fit together
A component interface is implemented by the core object and by decorators that hold another component and extend behavior through delegation.
Java example in the Order Management domain
package org.javaomnibus.ecommerce.gof.structural;
public interface CheckoutStep {
void run(Order order);
}
public final class CoreCheckoutStep implements CheckoutStep {
@Override
public void run(Order order) {}
}
public abstract class CheckoutStepDecorator implements CheckoutStep {
protected final CheckoutStep next;
protected CheckoutStepDecorator(CheckoutStep next) {
this.next = next;
}
}
public final class AuditCheckoutStep extends CheckoutStepDecorator {
public AuditCheckoutStep(CheckoutStep next) {
super(next);
}
@Override
public void run(Order order) {
next.run(order);
audit(order);
}
private void audit(Order order) {}
}
Step by step
- CoreCheckoutStep provides the base behavior.
- AuditCheckoutStep preserves the same interface while layering extra work.
- Multiple decorators can be stacked without exploding subclasses.
- Decorator is strongest when the force is optional layered behavior rather than access control or translation.
Where this pattern helps
- When behavior should be added selectively and in combinations
- When inheritance would create too many variant subclasses
When a simpler design is better
- When behavior does not need to be layered dynamically
- When the wrapper is actually about access mediation or interface translation instead
What this pattern costs
- Many small wrappers can make debugging call paths harder
- The order of decorators can affect behavior and must be designed carefully
How this fits today
Decorator remains highly relevant in Java for I/O streams, middleware chains, observability layers, and optional business behavior around stable contracts.
Where teams get it wrong
Using Decorator when a wrapper changes access rules or translates interfaces confuses it with Proxy or Adapter and weakens the design discussion.