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.