← Back to Additional Topics

Inheritance vs Composition

Additional TopicsMid

The Question

When should you use inheritance vs composition?

What a Strong Answer Covers

  • is-a vs has-a." "Favor composition." When to use each.

Senior-Level Answer

Inheritance and composition are both mechanisms for code reuse and building relationships between types, but they couple classes differently and suit different design problems.

**Inheritance (is-a relationship)** models a subtype relationship. `Dog extends Animal` means a Dog IS-A Animal and gets all of Animal's behavior. The subclass can override or extend parent behavior. The coupling is deep: the subclass is bound to the parent's implementation details, not just its interface. Changes to the parent class propagate to all subclasses, often unexpectedly.

**Composition (has-a relationship)** models ownership. A `Car` HAS-A `Engine`. The Car class holds a reference to an Engine and delegates behavior to it. The coupling is loose — Car depends on Engine's interface, not its implementation. Swap a `GasEngine` for an `ElectricEngine` and Car doesn't change.

**Why prefer composition?**

1. **Flexibility at runtime**: composed objects can be swapped at construction time or even at runtime. Inheritance hierarchies are fixed at compile time. 2. **Testability**: you can inject a mock Engine into Car. You can't easily mock a parent class without framework support. 3. **Avoiding the fragile base class problem**: changing a base class method signature or behavior can silently break all subclasses — a well-known danger in deep hierarchies. 4. **Avoiding the diamond problem**: multiple inheritance of implementation causes ambiguity (Python has MRO to resolve it; Java disallows it for classes). Composition sidesteps this. 5. **Single Responsibility**: behavior is encapsulated in focused components rather than bloated base classes.

**When to use inheritance:** - The relationship is genuinely polymorphic: you need to treat `Dog` and `Cat` as `Animal` via a common interface - The subclass truly IS-A supertype and the Liskov Substitution Principle holds (a `Dog` can substitute for an `Animal` everywhere) - Framework contracts require it (Django's `View` class, Java's `Thread`) - The hierarchy is shallow (1-2 levels) and stable

**The rule of thumb**: if you are inheriting to reuse code rather than to model a true subtype relationship, prefer composition. Abstract classes or interfaces define contracts; composition provides implementation reuse without coupling. The Gang of Four's advice — "favor object composition over class inheritance" — remains as relevant as ever.

Key Differences

AspectInheritanceComposition
RelationshipIs-a (subtype)Has-a (ownership/delegation)
CouplingTight (impl details exposed)Loose (interface only)
FlexibilityFixed at compile timeSwappable at construction/runtime
TestabilityHard (mocking base class)Easy (inject mock dependencies)
Behavior reuseVia override/extensionVia delegation
FragilityFragile base class problemIsolated — changes don't cascade
Use whenTrue LSP-compliant subtypeCode reuse, flexibility, testability

What Separates a 2/3 from a 3/3

2/3 — Passing but Incomplete

Correctly defines is-a vs. has-a relationship, explains the fragile base class problem, and gives a concrete example of refactoring inheritance to composition.

3/3 — Strong Answer

Covers Liskov Substitution Principle as the criterion for valid inheritance, explains runtime flexibility of composition, addresses testability with dependency injection, and cites Gang of Four guidance.

Common Mistakes

  • Using inheritance purely for code reuse without a true is-a relationship — the most common violation.
  • Saying 'always use composition' — ignoring cases where polymorphism genuinely requires inheritance or interface implementation.
  • Not mentioning the fragile base class problem as the main practical argument against deep inheritance hierarchies.
  • Confusing interface inheritance (implementing a contract) with implementation inheritance (extending a class) — the former is always fine, the latter requires caution.

Follow-Up Questions

  • What is the Liskov Substitution Principle and how does it tell you when inheritance is appropriate? — A subclass must be usable everywhere its superclass is expected without breaking the program — if it can't, the inheritance relationship is wrong.
  • How would you refactor a deep inheritance hierarchy (5 levels deep) to use composition? — Identify behaviors, extract them as interfaces with concrete implementations, replace extends with has-a fields, use constructor injection — start from the leaves.
  • Can you use both inheritance and composition in the same design? — Yes — inherit from an abstract base for the contract/polymorphism, use composition internally for implementation — decorator and strategy patterns do exactly this.
  • How does the Strategy pattern apply the principle of favoring composition over inheritance? — Instead of subclassing to vary behavior, you inject a strategy object — the context has-a strategy, which can be swapped without changing the context class.

Related Questions

  • CAP theorem
  • SQL vs NoSQL
  • Big O basics
  • N+1 problem
  • AI tools in workflow

Can You Explain This Cold?

Reading the answer is step one. Explaining it unprompted — under interview pressure — is what actually matters. Get AI-graded feedback on your answer with follow-up probes on your weak points.

Get Graded — Free Assessment
GrindQuestionsAITechnical interview assessment
TermsPrivacyAbout