Composition over Inheritance

Composition over inheritance naturally aligns with SOLID by keeping responsibilities focused, preserving substitutability, and allowing behavior to evolve independently without breaking existing code.
Inheritance models identity
Composition models capability

“Composition over inheritance helps us avoid LSP violations by ensuring classes only expose behaviors they can actually support. Instead of forcing subclasses to override or disable base behavior, we delegate varying behavior to composed components, resulting in more flexible, maintainable, and scalable designs.”

I generally prefer composition over inheritance when behavior varies, because inheritance can easily violate the Liskov Substitution Principle.

Inheritance works well only when there’s a true ‘is-a’ relationship and the behavior is stable across all subclasses. But in real systems, that assumption often breaks down.

A classic example is a Bird base class with a fly() method. Once you introduce a Penguin, you’re forced to override fly() or throw an exception. At that point, the subclass is no longer a valid substitute for the base class, which violates LSP.

Composition solves this by modeling capabilities instead of assumptions. Instead of saying all birds can fly, we say some birds have flying behavior. So we extract flying into a separate behavior and compose it where applicable.

This approach has several benefits:

That said, inheritance isn’t bad. I still use it when the abstraction is stable and universally true. But once I see empty overrides, NotImplementedException, or flags controlling behavior, that’s usually a signal to switch to composition.

In short, inheritance models identity, while composition models capability—and capability tends to change more often in real systems.


Below is a holistic, senior-level interview note on “Composition over Inheritance”, structured the way interviewers expect you to think, explain, and apply the concept.

Composition over Inheritance — Senior Software Engineer Interview Notes

1. Definition (Interview-Ready)

Composition over inheritance is a design principle where behavior is built by combining smaller, focused components instead of inheriting behavior from a base class.

The goal is to reduce coupling, increase flexibility, and avoid violating SOLID principles, especially Liskov Substitution Principle (LSP).

In short: “Prefer has-a over is-a when behavior varies.”


2. Why Inheritance Breaks Down at Scale

Inheritance works well only when behavior is stable and universally valid across subclasses.

Typical Problems with Inheritance

These issues usually appear after the system grows, not at the start.


LSP states:

Objects of a superclass should be replaceable with objects of its subclasses without breaking correctness.

Inheritance Violation Example

❌ Penguin is not a valid substitute for Bird

❌ LSP is violated


4. Composition as the Solution

Instead of inheriting behavior, extract varying behavior into separate components.

Design Shift

Core Idea


5. Conceptual Example

interface FlyBehavior {
  fly(): void;
}

class FlyWithWings implements FlyBehavior {
  fly() { console.log("Flying"); }
}

class NoFly implements FlyBehavior {
  fly() { /* no-op */ }
}

class Bird {
  constructor(private flyBehavior: FlyBehavior) {}

  fly() {
    this.flyBehavior.fly();
  }
}

Why This Works


6. Benefits (Senior-Level Talking Points)

1. LSP Compliance

2. Flexibility

3. Better SRP (Single Responsibility)

4. Open/Closed Principle

5. Reduced Coupling


7. When to Prefer Composition (Strong Signal Answer)

Use composition when:


8. When Inheritance Is Still Appropriate

Inheritance is not bad, just easy to misuse.

Use inheritance when:

Example: ArrayList is a List


9. Real-World System Design Analogy

Payment System

❌ PaymentProcessor → CreditCardPayment → PayPalPayment

✅ PaymentService has PaymentStrategy

Why?


10. Common Interview Follow-Ups

Q: Is composition always better than inheritance?

A: No. Composition is preferred when behavior varies. Inheritance is fine when the abstraction is stable and universally valid.


Q: Does composition add complexity?

A: Slight upfront complexity, but significantly reduces long-term maintenance cost and design rigidity.


11. One-Minute Interview Answer

“Composition over inheritance helps us avoid LSP violations by ensuring classes only expose behaviors they can actually support. Instead of forcing subclasses to override or disable base behavior, we delegate varying behavior to composed components, resulting in more flexible, maintainable, and scalable designs.”


12. Senior-Level Red Flags to Call Out