Skip to content

Composition over Inheritance — Detailed#

The inheritance trap#

classDiagram
  class Animal {
    +eat()
    +sleep()
  }
  class Bird {
    +fly()
    +eat()
    +sleep()
  }
  class Fish {
    +swim()
    +eat()
    +sleep()
  }
  class Penguin {
    +eat()
    +sleep()
    +swim()
    +fly()  -- throws UnsupportedOperationException
  }
  Animal <|-- Bird
  Animal <|-- Fish
  Bird <|-- Penguin

A Penguin is a Bird but can't fly. Either: - override fly() to throw (Liskov-violating); or - restructure the hierarchy (and rebuild your codebase).

The composition fix#

classDiagram
  direction LR
  class Animal {
    -mover: MoveBehavior
    +move()
  }
  class MoveBehavior {
    <<interface>>
    +move()
  }
  class Fly
  class Swim
  class Walk
  MoveBehavior <|.. Fly
  MoveBehavior <|.. Swim
  MoveBehavior <|.. Walk
  Animal o--> MoveBehavior

Each animal is composed of what it can do. A penguin gets Swim + Walk, no Fly. New behaviour = new class, no hierarchy edits.

Rules of thumb#

  • "is-a" → inheritance (rare).
  • "has-a" / "can do" → composition (default).
  • If you're inheriting only to share code, prefer composition + delegation.
  • If you're inheriting to be the parent type in the type system, inheritance is fine — but watch LSP.

Mixin / trait alternatives#

Languages like Scala (traits), Rust (impl blocks), TypeScript (intersection types), Python (mixins) let you mix in capabilities without single-line inheritance.

Where this shows up in this site#

  • Chess enginePiece doesn't subclass MoveStrategy; it has a move strategy.
  • Notification systemNotification doesn't extend EmailDeliverer; it composes a Channel.
  • Logger frameworkLogger composes appenders + formatters instead of inheriting from them.
  • Vending machine / Hotel management — pricing, dispatch, and state machines composed in.

Glossary & fundamentals#

Concepts referenced in this design. Each row links to its canonical page; the tag column shows whether it is a high-level (HLD) or low-level (LLD) concept.

Tag Concept What it is Page
HLD Observability metrics, logs, traces, SLOs observability
LLD State machines FSM, HSM, transitions, guards state-machines
LLD Async models futures / async-await / coroutines / actors async-models
LLD OOP pillars encapsulation, abstraction, inheritance, polymorphism oop-pillars
LLD SOLID principles SRP / OCP / LSP / ISP / DIP solid-principles
LLD Behavioural patterns Strategy, Observer, State, Command, Chain behavioral-patterns
LLD Composition over inheritance prefer composition; small swappable parts composition-over-inheritance
LLD Error handling exceptions vs Result, error boundaries error-handling