Composition Over Inheritance
"Favor composition over inheritance" is one of the most cited principles in object-oriented design, yet inheritance remains overused. This article explores practical patterns for achieving flexibility through composition: delegation, decorator, composite, and mixins.
📋 At a Glance
| Aspect | Details |
|---|---|
| Patterns Covered | Delegation, Decorator, Composite, Forwarding |
| Primary Benefit | Flexible runtime behavior, reduced coupling |
| Trade-offs | More objects, indirection |
| Java Features | Default methods, Records |
🎯 What You'll Learn
- Delegation Pattern for forwarding behavior
- Decorator Pattern for adding responsibilities dynamically
- Composite Pattern for tree structures
- Mixin simulation with default methods
- When inheritance is still appropriate
Production Story: The Inheritance Nightmare
A payment processing system started with a simple hierarchy:
JAVA(4 lines)CodeLoading syntax highlighter...
Three years later:
JAVA(9 lines)CodeLoading syntax highlighter...
- Diamond inheritance problem (Java doesn't support multiple inheritance)
- Changes to
Paymentbroke all 15 subclasses - Testing required understanding entire hierarchy
- Adding new features required new subclasses
JAVA(15 lines)CodeLoading syntax highlighter...
Mental Model: Inheritance vs Composition
TEXT(37 lines)CodeLoading syntax highlighter...
🔬 Deep Dive
Pattern 1: Delegation
JAVA(37 lines)CodeLoading syntax highlighter...
JAVA(31 lines)CodeLoading syntax highlighter...
Pattern 2: Decorator Pattern
JAVA(91 lines)CodeLoading syntax highlighter...
JAVA(9 lines)CodeLoading syntax highlighter...
Pattern 3: Composite Pattern
JAVA(95 lines)CodeLoading syntax highlighter...
Pattern 4: Mixins with Default Methods
JAVA(53 lines)CodeLoading syntax highlighter...
JAVA(55 lines)CodeLoading syntax highlighter...
Pattern 5: Strategy as Composition
JAVA(48 lines)CodeLoading syntax highlighter...
When to Use Inheritance
Inheritance is appropriate when:
JAVA(48 lines)CodeLoading syntax highlighter...
Decision Guide: Inheritance vs Composition
TEXT(32 lines)CodeLoading syntax highlighter...
⚠️ Common Mistakes
Mistake 1: Inheriting for Code Reuse Only
JAVA(24 lines)CodeLoading syntax highlighter...
Mistake 2: Deep Inheritance Hierarchies
JAVA(14 lines)CodeLoading syntax highlighter...
🐛 Debug This: The Decorator Disaster
A developer reports: "Our logging decorator works fine alone, but when we combine it with the caching decorator, the logs show incorrect values!"
JAVA(53 lines)CodeLoading syntax highlighter...
LoggingDecorator.getData() - it calls delegate.getData(key) twice:- Once to get the result for logging
- Once again to return the value
This causes:
- With LoggingDecorator → CachingDecorator: First call hits cache miss, second call gets cached value (different!)
- With CachingDecorator → LoggingDecorator: Each call to logging generates two database calls
JAVA(14 lines)CodeLoading syntax highlighter...
💻 Exercises
Exercise 1: Forwarding Wrapper
⭐ Difficulty: Easy | ⏱️ Time: 15 minutes
List.JAVA(11 lines)CodeLoading syntax highlighter...
JAVA(45 lines)CodeLoading syntax highlighter...
Exercise 2: Decorator Chain
⭐⭐ Difficulty: Medium | ⏱️ Time: 20 minutes
JAVA(18 lines)CodeLoading syntax highlighter...
JAVA(60 lines)CodeLoading syntax highlighter...
Exercise 3: Composite File System
⭐⭐ Difficulty: Medium | ⏱️ Time: 25 minutes
JAVA(25 lines)CodeLoading syntax highlighter...
JAVA(61 lines)CodeLoading syntax highlighter...
Exercise 4: Mixin Interfaces
⭐⭐⭐ Difficulty: Medium-Hard | ⏱️ Time: 20 minutes
JAVA(20 lines)CodeLoading syntax highlighter...
JAVA(56 lines)CodeLoading syntax highlighter...
Exercise 5: Refactor Inheritance to Composition
⭐⭐⭐⭐ Difficulty: Hard | ⏱️ Time: 30 minutes
JAVA(24 lines)CodeLoading syntax highlighter...
JAVA(106 lines)CodeLoading syntax highlighter...
🎤 Interview Questions
Q1: Why "favor composition over inheritance"?
- Composition is more flexible (runtime vs compile-time)
- Avoids tight coupling to parent class
- Enables combining multiple behaviors
- Easier to test (mock dependencies)
- Doesn't break encapsulation
Q2: When is inheritance still the right choice?
- True "is-a" relationship that won't change
- Framework extension points (designed for inheritance)
- Template Method pattern (defined extension points)
- Abstract classes with protected helpers
📝 Summary
| Pattern | Use When |
|---|---|
| Delegation | Reuse behavior without inheritance |
| Decorator | Add responsibilities dynamically |
| Composite | Tree structures, uniform treatment |
| Mixins | Share behavior across unrelated classes |
| Strategy | Behavior varies independently |
| Inheritance | True "is-a", framework extension |
📅 Review Schedule for This Article
| Day | Task | Time |
|---|---|---|
| Day 1 | Review decision flowchart (inheritance vs composition) | 5 min |
| Day 3 | Redo Exercise 2 (Decorator Chain) | 20 min |
| Day 7 | Answer interview questions without looking | 10 min |
| Day 14 | Redo Debug This (Decorator Disaster) | 15 min |
| Day 30 | Identify one inheritance hierarchy in your codebase to refactor | 20 min |