PrepZone Logo
PrepZone

Composition Over Inheritance

When to compose collaborators and when a subtype is the right model.

Why this matters

  • Deep inheritance hierarchies are the fastest path to rigid, untestable code.
  • Composition enables swapping collaborators — critical for Strategy and testing.
  • Interviewers often ask 'how would you add X without editing existing classes?'
  • CampusOS services favour has-a until is-a is provably stable.
Inheritance — is a
OrderServiceextends EmailSender
EmailSenderCannot be swapped
Composition — has a
OrderServiceholds a Notifier
EmailNotifier
SmsNotifier
Inheritance locks you into one parent for the object's whole life. Composition lets you swap the collaborator, which is what makes testing and change cheap.

Overview

When to compose collaborators and when a subtype is the right model. Study each section below in order — every list is interview-ready reference material.

The fragile base class problem

Points to cover

  • Parent class changes can break all subclasses unexpectedly
  • Subclass overrides may violate parent assumptions (LSP risk)
  • Deep trees make it hard to predict which method runs
  • Example: EmailSender subclass breaks when parent adds SMS logic
  • Fix: extract behaviour into composed collaborator

Composition for behaviour

Points to cover

  • Hold interface-typed fields; inject implementations
  • Swap Notifier implementation without changing OrderService
  • Test with mock collaborators instead of heavy subclasses
  • Compose multiple behaviours (logging + metrics) as wrappers
  • CampusOS: BookingService composes PricingStrategy, not extends it

Interface-based design

Points to cover

  • Program to interfaces — callers depend on capability names
  • Small interfaces (ISP) — Reader, Writer not IOEverything
  • Enables fake implementations in unit tests
  • Factory or DI container wires concrete classes

Interview signals

Points to cover

  • Asked to add payment method → prefer new class over editing switch
  • Asked about testing → mention injected interfaces
  • Deep inheritance diagram → interviewer may probe LSP
Java
// CampusOS — apply "The fragile base class problem" on paper first, then in code
public final class CompositionOverInheritanceExample {
    // 1. Name entities from the problem statement
    // 2. Draw relationships before writing methods
    // 3. Introduce patterns only when a force appears
}

Quick recall

Everything you need if you only revisit this box.

  1. The fragile base class problem — review this slice before mock interviews.
  2. Composition for behaviour — review this slice before mock interviews.
  3. Interface-based design — review this slice before mock interviews.
  4. Interview signals — review this slice before mock interviews.

Test yourself

Answer these before moving on — recall is what makes it stick.