SOLID Liskov Substitution Principle What is it? |Video upload date:  · Duration: PT8M12S  · Language: EN

Clear explanation of the Liskov Substitution Principle from SOLID with examples pitfalls and practical design tips for safe subclassing.

If you like surprises in production then congratulations you can ignore the Liskov Substitution Principle and enjoy debugging at 2 AM. For the rest of us who prefer software that behaves like it was designed on purpose the Liskov idea is simple and useful. In SOLID and OOP design this principle says that a subtype must behave like its base type so client code does not get unpleasant surprises.

What the principle actually expects

A base type defines a contract about inputs outputs and object state after methods run. Subclasses must honor that contract. If a subclass tightens allowed inputs or breaks promised outputs then polymorphism stops working and callers get wrong results instead of polite exceptions.

Classic broken example

The infamous Square inherits from Rectangle story is still the sad clown of inheritance mistakes. A square that mutates width and height together changes setter semantics and invalidates code that expects independent setters. Observable behavior like area becomes wrong and that is a violation of LSP.

class Rectangle {
  setWidth(w) {}
  setHeight(h) {}
  area() {}
}

class Square extends Rectangle {
  setWidth(s) { super.setWidth(s) super.setHeight(s) }
  setHeight(s) { super.setWidth(s) super.setHeight(s) }
}

Practical rules to keep your code honest

  • Define clear contracts by documenting expected inputs outputs and side effects so subclass authors do not invent surprises
  • Avoid strengthening preconditions in subclasses because client code will pass values allowed by the base type
  • Preserve postconditions and invariants so callers can rely on results and object state after calls
  • If a subtype needs different behavior prefer composition and delegation over inheritance
  • Write tests that use subclasses through base type interfaces and assert observable behavior remains consistent

Composition is not a betrayal

If the subtype behaves differently enough then composition with a small shared interface is often the cleaner choice. Composition keeps responsibilities explicit and avoids accidental contract drift. This is core to clean code and helps during refactoring when the temptation to copy paste and tweak wins.

Testing patterns that catch violations

Unit tests that treat derived objects as base type instances are your best early warning system. Create tests that exercise the contract not the implementation. Put instances through the same scenarios a client would and verify outputs invariants and side effects. If a test fails you found a substitutability problem before users did.

Short checklist before you inherit

  • Can every client of the base type treat the candidate subtype as if it were the base type
  • Does the subtype accept the same range of inputs and return the same guarantees
  • Will invariants hold after method calls on the subtype
  • Would composition make the intention clearer and safer

Follow these LSP guidelines and your OOP designs will play nicely with polymorphism and refactoring. Keep contracts small explicit and tested and your future self will stop leaving passive aggressive comments in code reviews. That is good for software design and surprisingly kind to humans.

I know how you can get Azure Certified, Google Cloud Certified and AWS Certified. It's a cool certification exam simulator site called certificationexams.pro. Check it out, and tell them Cameron sent ya!

This is a dedicated watch page for a single video.