100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace

Why Favor Composition Over Inheritance?

Why composition beats inheritance in OOP — the fragile base class problem, runtime flexibility — explained with a Java example.

mediumQ148 of 226 in Object Oriented Programming Est. time: 5 minsLast updated:
Open Code Lab

Expected Interview Answer

Composition is generally favored over inheritance because it produces looser coupling and more flexible designs: composed behavior can be swapped at runtime through interfaces, while inherited behavior is locked into a rigid, compile-time class hierarchy.

Inheritance exposes a subclass to every implementation detail of its superclass, so a change in the parent can silently break children (the fragile base class problem), and deep hierarchies become hard to reason about or refactor. Composition instead lets an object hold a reference to another object through an interface and delegate to it, meaning the concrete implementation can change, or even change at runtime, without touching the class that uses it. This is not an absolute ban on inheritance — genuine is-a relationships with true substitutability (satisfying the Liskov Substitution Principle) are still well served by it — but composition should be the default choice when the relationship is really has-a or the hierarchy would need multiple, conflicting dimensions of specialization.

  • Avoids the fragile base class problem
  • Enables swapping behavior at runtime via interfaces
  • Prevents deep, hard-to-refactor inheritance hierarchies
  • Keeps classes reusable across unrelated contexts

AI Mentor Explanation

If a "SpinBowlerBatsman" class had to inherit from both a SpinBowler class and a Batsman class, any change to the base SpinBowler’s run-up logic could silently break every all-rounder subclass built on top of it. Instead, a Player composed of a separate BowlingStyle object and a BattingStyle object lets you change the bowling style component without ever touching the Player class. That is why composition is favored: it isolates change to the swapped-out component rather than rippling through an inherited chain.

Step-by-Step Explanation

  1. Step 1

    Spot multiple dimensions of variation

    Notice when a class would need to inherit two or more unrelated behaviors at once.

  2. Step 2

    Extract each dimension as an interface

    Model each varying behavior as its own small interface.

  3. Step 3

    Compose instead of extend

    Give the main class fields holding those interfaces rather than subclassing a combined base.

  4. Step 4

    Delegate and swap freely

    The class forwards calls to its composed parts, and any part can be replaced independently.

What Interviewer Expects

  • Mention of the fragile base class problem
  • Understanding this is guidance, not an absolute rule against inheritance
  • A concrete example showing multiple-dimension variation
  • Awareness of runtime flexibility as a key advantage of composition

Common Mistakes

  • Claiming inheritance should never be used at all
  • Not explaining why deep hierarchies become fragile
  • Confusing this guidance with the Liskov Substitution Principle itself
  • Giving no concrete example of the trade-off

Best Answer (HR Friendly)

We favor composition over inheritance because it keeps code more flexible — instead of locking behavior into a rigid parent-child class structure, you build objects out of smaller, swappable pieces they hold references to. That way, a change in one piece does not ripple unpredictably through a whole family of subclasses, and you can even change behavior at runtime.

Code Example

Multiple-dimension variation solved with composition
interface MovementBehavior {
    void move();
}

interface AttackBehavior {
    void attack();
}

class Flying implements MovementBehavior {
    public void move() { System.out.println("Flies through the air"); }
}

class FireBreath implements AttackBehavior {
    public void attack() { System.out.println("Breathes fire"); }
}

class Dragon {
    // Composition: two independent, swappable behaviors instead of
    // inheriting from both a FlyingCreature and a FireBreather base
    private MovementBehavior movement;
    private AttackBehavior attack;

    Dragon(MovementBehavior movement, AttackBehavior attack) {
        this.movement = movement;
        this.attack = attack;
    }

    void act() {
        movement.move();
        attack.attack();
    }
}

Dragon dragon = new Dragon(new Flying(), new FireBreath());
dragon.act();

Follow-up Questions

  • What is the fragile base class problem?
  • Can you give an example where inheritance is still the better choice?
  • How does the Strategy pattern relate to this guidance?
  • How does composition affect unit testing compared to inheritance?

MCQ Practice

1. The primary risk of deep inheritance hierarchies this guidance addresses is?

Changes in a superclass can silently break subclasses that depend on its internal behavior, known as the fragile base class problem.

2. Does “favor composition over inheritance” mean inheritance should never be used?

Inheritance remains appropriate for genuine, substitutable is-a relationships; composition is the recommended default elsewhere.

3. A key advantage composition has over inheritance is?

Because composed dependencies are referenced through interfaces, the concrete implementation can be swapped without altering the containing class.

Flash Cards

Why favor composition over inheritance?Looser coupling, runtime flexibility, and avoidance of the fragile base class problem.

Fragile base class problem?A change in a superclass unexpectedly breaks subclasses relying on its internals.

Is inheritance ever still appropriate?Yes, for genuine, substitutable is-a relationships.

How does composition handle multiple behaviors?Each behavior becomes its own composed, swappable object instead of a fused base class.

1 / 4

Continue Learning