Decorator vs subclassing
Fast recall
Quick revision
Core answer
Interview-ready guide
Interview answer
Subclassing and Decorator both extend behavior, but they model different relationships. A subclass is a subtype: a concrete object is constructed as that class and overridden methods dispatch at runtime. A decorator has the same component contract, holds another component, and adds work around delegation. For example, new CompressingStream(new BufferedStream(file)) assembles two responsibilities without defining a BufferedCompressedFileStream subclass. This is useful when independent features combine in different orders. Inheritance does not always cause exponential classes; that happens when optional feature combinations are forced into the hierarchy. Decorator also has costs because wrapper order, identity, and stack traces can become harder to follow.
Decorator vs subclassing
Direct Answer
Subclassing specializes a type through inheritance; Decorator composes another object with the same contract and adds responsibility around delegation. Both can be selected at runtime, but an object's class does not change after construction, while wrappers can be assembled in different combinations. Class explosion occurs only when inheritance models many feature combinations, not from inheritance itself.
Code Example
// SUBCLASSING: behavior fixed at compile time; combinations explode
class Coffee { double cost(){ return 2.0; } }
class MilkCoffee extends Coffee { double cost(){ return super.cost() + 0.5; } }
class MilkSugarCoffee extends MilkCoffee { double cost(){ return super.cost() + 0.2; } }
// Every new combo (soy? caramel?) needs yet another subclass.
// DECORATOR: behavior added at runtime by wrapping; combos stay linear
interface Bev { double cost(); }
class Plain implements Bev { public double cost(){ return 2.0; } }
class Milk implements Bev {
private final Bev inner; Milk(Bev b){ this.inner = b; }
public double cost(){ return inner.cost() + 0.5; }
}
class Sugar implements Bev {
private final Bev inner; Sugar(Bev b){ this.inner = b; }
public double cost(){ return inner.cost() + 0.2; }
}
// new Sugar(new Milk(new Plain())) -> compose any combination at runtime.
Common Mistakes & Pitfalls
- Using subclassing for optional features: Freshers create subclasses like ServiceWithLogging, ServiceWithCaching, ServiceWithLoggingAndCaching—this creates exponential class growth and tight coupling between concerns. Use Decorator for optional features that combine.
- Using Decorator for type hierarchies: Wrapping a Car with a VehicleDecorator to turn it into a Truck misses that these are distinct types with different contracts—inheritance is correct for modeling type relationships.
- Thinking they're syntactically equivalent: Saying inheritance is just syntax and Decorator is just composition ignores that they have fundamentally different runtime behavior—inheritance is static and Decorator is dynamic.
- Not considering the Open-Closed Principle: Using subclassing means adding new behavior requires modifying the class hierarchy, while Decorator lets you add behavior by writing new decorators without touching existing code.
Check your understanding
Answer these without looking back at the article.
- Can you use both inheritance and Decorator pattern together in the same design?
- How does composition-over-inheritance relate to the Decorator pattern?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.