advanced

Liskov Substitution Principle

Ensure implementations satisfy the same contract, preconditions, postconditions, and error expectations as the abstraction they replace.

LSP: subtypes must be substitutable for their base type without breaking callers—honor preconditions, postconditions, invariants, and error behavior. If `Bird.fly()` exists, `Penguin` must not throw unexpectedly when used as `Bird`.

Design by contract: callers trust the abstraction; violations cause subtle production bugs in polymorphic code.

On interviews: classic rectangle/square debate and how composition fixes the model.

Common pitfalls: strengthening preconditions in overrides, weakening postconditions, returning incompatible types, and empty override methods that do nothing meaningful.

The trade-off is strict subtype discipline versus tempting inheritance for reuse only.

Checklist:

  • Subtypes must not surprise callers.
  • Prefer composition when is-a is false.
  • Test substitutability with contract tests.