2.1.15OOP Fundamentals

Composition — has-a relationship vs is-a

1,811 words8 min readdifficulty · medium

WHAT is composition?

The everyday test:

Sentence that sounds natural Relationship Tool
"A car is an engine" ❌ nonsense
"A car has an engine" ✅ has-a composition
"A dog is an animal" ✅ is-a inheritance
"A dog has an animal" ❌ nonsense

WHY prefer composition? (the famous principle)

HOW composition wins concretely:

  1. Flexibility — you can swap the part at runtime (car.engine = ElectricEngine()).
  2. No combinatorial explosion — a FlyingSwimmingRobot doesn't need a class; give it a Flyer part and a Swimmer part.
  3. Encapsulation — the part's guts stay hidden.

Derivation-from-scratch: building "Car has-a Engine"

We derive the structure rather than memorize it.

Step 1 — name the responsibilities. A car must start(). Starting needs ignition logic. Why split? Because ignition is a self-contained concept that may change independently (petrol vs electric).

Step 2 — give the sub-responsibility its own class.

class Engine:
    def __init__(self, horsepower):
        self.horsepower = horsepower
    def ignite(self):
        return f"Engine roaring at {self.horsepower}hp"

Why this step? We isolate the volatile part so the Car never touches its internals.

Step 3 — the Car HOLDS an Engine (composition) and delegates.

class Car:
    def __init__(self, engine):          # engine passed IN
        self.engine = engine             # the has-a field
    def start(self):
        return "Car: " + self.engine.ignite()   # delegation

Why pass it in (dependency injection)? So we can give any engine — petrol, electric, mock — without editing Car.

Step 4 — use it.

c = Car(Engine(120))
print(c.start())     # Car: Engine roaring at 120hp
c.engine = Engine(300)   # swap at runtime — impossible with inheritance!
Figure — Composition — has-a relationship vs is-a

Worked examples


Common mistakes (Steel-manned)


Recall Feynman: explain to a 12-year-old

Imagine LEGO. Inheritance is like saying "a race-car is a special kind of car" — it's the same toy, just fancier. Composition is like building a car out of separate LEGO pieces: wheels, an engine block, a seat. If you don't like the engine, you pop it off and click a new one in — you don't rebuild the whole car. That popping-and-swapping is why builders love composition: the car just has an engine, it doesn't become one.


Flashcards

What relationship does composition model?
"has-a" — a class holds another class as a field and delegates to it.
What relationship does inheritance model?
"is-a" — the subclass is a kind of the superclass and must be substitutable for it.
The English test for is-a vs has-a?
Read "A is a B" vs "A has a B" aloud; pick whichever is grammatically true.
Why is "favor composition over inheritance" advised?
Composition couples loosely via a small public interface; inheritance tightly couples to the parent's internals and breaks on parent changes (fragile base class).
Why is class Stack(list) a bad is-a design?
It inherits list's full interface (insert, indexing, slicing), letting users violate LIFO. Use has-a + a hidden list instead.
How does composition avoid combinatorial class explosion?
Behaviors become swappable part-objects mixed at runtime, so you don't need a separate class per combination.
What is delegation?
A method forwards work to a held object, e.g. Car.start() calls self.engine.ignite().
What deep type rule must inheritance satisfy?
Liskov substitution — a subclass must work anywhere the superclass is expected.
Runtime advantage unique to composition?
You can swap the held part while the program runs (car.engine = ElectricEngine()); inheritance is fixed at compile time.

Connections

  • Inheritance — is-a relationship
  • Liskov Substitution Principle
  • Encapsulation and Information Hiding
  • Dependency Injection
  • Strategy Pattern (composition of swappable behaviors)
  • Aggregation vs Composition (UML)
  • Fragile Base Class Problem

Concept Map

has-a

is-a

decides

mirrors

justifies

holds part as

couples

couples

risks

enables

leads to

motivates

New class design question

Composition

Inheritance

English grammar test

Liskov substitutability

Field + delegation

Loose coupling

Tight coupling

Fragile base class

Swap, no explosion, encapsulation

Favor composition over inheritance

Hinglish (regional understanding)

Intuition Hinglish mein samjho

Dekho, jab tum koi naya class banate ho, do hi sawaal poochne hote hain: kya yeh cheez kisi cheez ko rakhti hai (has-a), ya kya yeh kisi cheez ka type hai (is-a)? Agar "Car has-a Engine" natural lagta hai, to Engine ko ek field ki tarah store karo aur uske methods ko call karo — isko composition kehte hain. Agar "Dog is-a Animal" natural lagta hai, to inheritance use karo. Bas English mein sentence bol ke check kar lo — grammar tumhe design bata degi.

Ab sabse important baat: log kehte hain "favor composition over inheritance". Kyun? Kyunki inheritance mein child class apne parent ke saare internals inherit kar leti hai, aur agar parent badla to child silently toot sakta hai (fragile base class problem). Composition mein tum sirf part ke chote se public interface pe depend karte ho — yeh loosely coupled hai, isliye safe hai. Aur ek bonus: tum runtime pe part swap kar sakte ho, jaise car.engine = ElectricEngine() — inheritance mein yeh impossible hai.

Classic galti: class Stack(list) likhna kyunki "stack ek list hi to hai". Galat! Ab user stack.insert(0, x) ya stack[5] kar ke beech mein ghus sakta hai aur LIFO break ho jaata hai. Iska fix — Stack ke andar ek private list rakho (has-a), aur sirf push/pop/size public rakho. Yaad rakho mnemonic: "HAS a part, IS a kind" — agar confuse ho to has-a hamesha safe choice hai.

Go deeper — visual, from zero

Test yourself — OOP Fundamentals