2.2.6Design Principles

SOLID — Liskov Substitution Principle

2,066 words9 min readdifficulty · medium3 backlinks

WHY does LSP exist?

The L in SOLID comes from Barbara Liskov (1987). Her formal statement:


WHAT exactly must a subclass preserve? (The contract)

A subclass overriding a method must respect the behavioural contract, not just the method signature. Three rules:

WHY those directions? (Derivation from the caller's view)


Figure — SOLID — Liskov Substitution Principle

HOW to spot a violation — the classic Rectangle/Square


Common mistakes (Steel-manned)


Flashcards

LSP in one sentence
Subtypes must be substitutable for their base type without breaking program correctness.
Who formulated LSP
Barbara Liskov (1987).
Can a subclass strengthen a precondition?
No — preconditions may only be weakened/widened (must accept ≥ the base's inputs).
Can a subclass weaken a postcondition?
No — postconditions may only be strengthened/narrowed (must guarantee ≥ the base's promises).
Why are preconditions contravariant and postconditions covariant?
Because a caller who only knows the base sends base-legal inputs and relies on base-promised outputs; widening inputs / narrowing outputs never surprises that caller.
Why does Square extends Rectangle violate LSP
setWidth/setHeight on a mutable Square change the other dimension, breaking the invariant that width and height are independent that a Rectangle caller relies on.
Why is Penguin extends Bird (with fly()) an LSP violation
Penguin.fly() throws an exception not in Bird's contract (rule 3: no new exception types).
A common fix when a subtype can't honor a method
Restructure the hierarchy / split interfaces so that subtype is never substitutable where the capability is required.
LSP enables which feature to actually work
Polymorphism — coding once against a base type and trusting all subtypes.

Recall Feynman: explain to a 12-year-old

Imagine a job posting that says "We need someone who can drive a car." You hire whoever shows up, trusting they can drive. LSP says: if you call someone a "Driver," they'd better actually drive — you shouldn't get a person who, when you hand them keys, screams "I can't drive!" or who can only turn left. A "subclass" is like a more specific kind of Driver (a taxi driver, a race driver). Each one must be able to do everything a normal Driver can do, or your plans fall apart. If a "Penguin" claims to be a "Bird that flies" but can't fly, don't call it that — give it a different label so nobody hands it a sky-job.

Connections

Concept Map

only pays off if

means

must respect

rule 1

rule 2

rule 3

derives

derives

contravariant widen

covariant narrow

when violated

destroys

Inheritance plus Polymorphism

Liskov Substitution Principle

Subclass replaces base

Behavioural contract

Preconditions cannot strengthen

Postconditions cannot weaken

Invariants preserved

Caller knows only base

Polymorphism preserved

Type-check hacks everywhere

Hinglish (regional understanding)

Intuition Hinglish mein samjho

LSP ka matlab simple hai: agar tumhara code kisi base class ke against likha gaya hai, to tum usme koi bhi subclass ka object daalo, program toot-na nahi chahiye. Subclass ek vaada hai — "main woh sab kar sakta hoon jo mera parent kar sakta tha, usi tarah." Agar subclass chupke se behaviour badal de (jaise exception throw kar de ya alag result de), to caller ko har jagah if-else lagana padega, aur tabhi to inheritance ka poora fayda khatam ho jaata hai.

Sabse famous example: Square extends Rectangle. Maths me square ek rectangle hota hai, par code me dhokha hai. Rectangle me setWidth(5) aur setHeight(4) karoge to area 20 aana chahiye. Lekin Square me width set karne se height bhi change ho jaati hai, to area 16 aa jaata hai. Caller ne jo property maani thi (20) woh fail ho gayi — yahi LSP violation hai. Solution: dono ko immutable banao ya ek common Shape interface use karo jisme sirf area() ho.

Do golden rules yaad rakho — Wide In, Narrow Out (WINO). Subclass ko inputs me zyada accept karna chahiye (preconditions weaken kar sakte ho), aur outputs me kam se kam utna dena chahiye jitna base deta tha (postconditions strengthen kar sakte ho). Ulta karoge — input restrict kiya ya output kam diya — to base ke caller ko surprise milega aur crash hoga.

Yeh principle important kyun hai? Kyunki yeh polymorphism ko bachata hai. Polymorphism ka pura sapna yeh hai ki ek baar base ke against code likho aur sab subtypes ke saath chal jaaye. LSP woh guarantee deta hai. Penguin wala example bhi yaad rakho: agar Penguin Bird se extend karke fly() me exception phenke, to galat hai — hierarchy todo: FlyingBird alag banao, Penguin sirf Bird se extend kare.

Go deeper — visual, from zero

Test yourself — Design Principles

Connections