OOD & LLD Reference/OOD Foundations

Composition vs Inheritance

Favor object composition over class inheritance — when inheritance is appropriate (true is-a), when it creates fragile hierarchies, and how delegation replaces deep trees.

2/5Overview: 20m

The is-a vs has-a decision

Inheritance shares implementation and establishes a subtype relationship. Composition embeds behavior and delegates to it. GoF's default advice — favor composition — exists because inheritance couples you to a parent's implementation details and freezes the hierarchy at compile time.

SignalPrefer inheritancePrefer composition
RelationshipTrue, stable is-a (every SavingsAccount is an Account)Behavior varies independently (Bird has-a FlyBehavior)
Extension axisFixed taxonomy, few variantsOpen-ended variants (pricing, payment, eviction)
Runtime swapNever neededNeeded (strategy swap, feature flags)
Depth≤ 2 levelsAny depth via delegation chains

Fragile base class and the diamond problem

When a subclass overrides onUpdate() and the parent adds a new code path that forgets to call it, subclasses break without compiling. Composition avoids this: the wrapper controls all delegation. Multiple inheritance (or default interface methods) creates ambiguous resolution — another reason LLD interviews favor interfaces + composition over mixin towers.

Delegation pattern in practice

Replace class FlyingBird extends Bird with class Bird { private MovementBehavior movement; }. New behaviors = new classes, zero edits to Bird. Decorator and Strategy are composition dressed for specific jobs — wrapping and algorithm pluggability respectively.

When inheritance is still correct

Template Method (AbstractClass.process() calling abstract hook()) and true domain taxonomies (CheckingAccount extends Account with shared transfer()) are legitimate. The test: would a caller ever need to swap this behavior at runtime? If yes, composition.

Senior-level signal

When asked "why not inherit?", say: "Inheritance leaks the parent's contract — if PremiumMember extends Member but can't use borrow() the same way, LSP breaks. I'd model Member with a MembershipPolicy composed object instead." That sentence hits LSP, composition, and DIP in one breath.

Where this goes next

Classes, Interfaces & Cohesion turns these structural choices into crisp public APIs — how small to make classes, when to use abstract classes vs interfaces, and what callers actually see.

Further Reading

Practice Tasks (Optional)

Design or implement locally in any language — no autograding. Focus on class structure, extensibility, and being able to explain trade-offs out loud.

  • Refactor an inheritance hierarchy to composition

    On paper: draw a 3-level inheritance tree (e.g. Bird → FlyingBird → Eagle). Redesign using a Bird class that composes a FlyBehavior interface. List what becomes easier to extend.

    20m