SDE Path

Foundations

Composition Over Inheritance

Why deep class hierarchies collapse under changing requirements, and how to model behavior with composition instead.

10 min4 steps · 4 checks
Step 1 of 40/4 checks correct

Inheritance looks efficient in the first five minutes of a machine-coding round and expensive for the next fifty. The moment a subclass inherits a method it should not have, you are maintaining a lie about the type.

Consider a naive vehicle hierarchy:

class Vehicle {
    void startEngine() { /* ... */ }
    void refuel(int litres) { /* ... */ }
}

class Car extends Vehicle { }

class Bicycle extends Vehicle {
    // A bicycle has no engine and no fuel tank, yet it inherits both.
    // startEngine() and refuel() are now nonsense operations on a Bicycle.
}

The hierarchy models "is-a" by name but not by behavior. Every caller that holds a Vehicle reference can call refuel on a Bicycle, and you are forced to either throw at runtime or silently no-op. Both are design smells the interviewer will notice.

The root cause: inheritance couples you to everything the base class does, not just the parts that actually apply. When requirements shift — an electric car, a cargo bike — the base class becomes a battleground of if checks and overrides.

Quick check

Why is Bicycle extends Vehicle a poor model when Vehicle defines refuel()?