"If you need to write if (object is Subclass) at runtime, your inheritance might already be wrong."
The Liskov Substitution Principle (LSP) is often seen as academic — until it quietly breaks your system in production.
You extend a class thinking you're reusing code. But without realizing, you're creating a subclass that violates the expected behavior of the base class.
It compiles. It runs. But it doesn’t behave.
That’s what the Liskov Substitution Principle, the “L” in SOLID, helps us prevent.
What does LSP actually say?
“If S is a subtype of T, then objects of type T may be replaced with objects of type S without altering the correctness of the program.”
— Barbara Liskov (1987)
In simple terms:
A subclass must preserve the behavior expected from the base class
You should be able to use it without knowing it’s a subclass
If you need to adjust your logic to account for the subclass… it's broken
A simple analogy: plastic cup vs glass cup
Imagine a machine that fills cups with water.
If you replace a plastic cup with a glass one, everything should still work the same.
If the glass shatters because the system wasn’t expecting something so fragile… that’s a substitution violation.
Same goes for code: changing the internal behavior silently breaks trust in the abstraction.
A classic example: Rectangle vs Square
It sounds harmless.
After all, a square is a rectangle with equal sides, right?
public class Rectangle {
public virtual double Width { get; set; }
public virtual double Height { get; set; }
public double Area() => Width * Height;
}
Now we extend it:
public class Square : Rectangle {
public override double Width {
set {
base.Width = value;
base.Height = value;
}
}
public override double Height {
set {
base.Width = value;
base.Height = value;
}
}
}
Looks reasonable — but then:
Rectangle r = new Square();
r.Width = 5;
r.Height = 10;
Console.WriteLine(r.Area()); // Expected: 50 — Actual: 100 ❌
Boom. The behavior is broken because Square overrides properties in a way that breaks the base class’s assumptions.
How to tell you’re violating LSP
You need to check if (object is SubType) in your logic
Your subclass throws exceptions in methods expected to be safe
Your subclass changes the effect of base class methods
You can’t confidently substitute the base with the subclass
If you can’t trust the object without checking its internals, you’ve violated LSP.
The fix: Prefer composition over inheritance
Inheritance enforces contracts. Composition enables flexibility.
Let’s rewrite Rectangle and Square using interfaces:
public interface IShape {
double Area();
}
public class Rectangle : IShape {
public double Width { get; set; }
public double Height { get; set; }
public double Area() => Width * Height;
}
public class Square : IShape {
public double Side { get; set; }
public double Area() => Side * Side;
}
Now, each shape defines its own logic.
No forced behavior. No violations. No surprises.
**
Practical tips to apply LSP in your code**
Use interfaces to define clear expectations
Favor composition when behavior significantly differs
Write tests that swap subtypes for base types
Ask yourself: “If I replace this object with another of the same interface, will it still work as expected?”
A real-world case: payment validation
public class Payment {
public virtual void Validate() {
// basic checks
}
}
public class InternationalPayment : Payment {
public override void Validate() {
base.Validate();
CheckExchangeRates(); // might throw
}
}
If the system expects a Payment, and you pass InternationalPayment, unexpected side effects might occur — breaking behavior that should be consistent.
-Better: separate validators using interfaces:
public interface IPaymentValidator {
void Validate();
}
Each class handles its own logic — without corrupting a shared inheritance chain.
Conclusion: LSP is about trust in your architecture
Liskov Substitution Principle protects your system from fake inheritance — when things look similar but behave completely differently.
Ignoring LSP doesn’t throw compile errors.
It doesn’t warn you at runtime.
It just silently erodes your architecture until refactoring becomes a nightmare.
✔ If a subclass cannot safely replace the base class, it should not inherit from it.
Top comments (0)