L Liskov Substitution Principle LSP
2 min readL — Liskov Substitution Principle (LSP)
“Derived classes should be substitutable for their base classes.”
Derived classes must behave consistently with their base abstraction. If code works with a base type, it must also work with any derived type, without extra checks, exceptions, or special cases. Inheritance is a promise of behavior, not just shared code.
❌ Bad example:
public abstract class Order
{
public abstract void Cancel();
}
public class MarketOrder : Order
{
public override void Cancel() => Console.WriteLine("Cancelled");
}
public class InstantOrder : Order
{
public override void Cancel() => throw new NotSupportedException();
}
❌ Violates LSP — an InstantOrder cannot cancel, so substituting it breaks code.
âś… Good example:
public interface ICancelableOrder
{
void Cancel();
}
public class MarketOrder : ICancelableOrder { public void Cancel() { /* ... */ } }
đź’ˇ In trading systems: If you design a Strategy base class, ensure all derived strategies behave consistently and safely under the same interface.
Questions & Answers
A: When derived types throw or ignore base contract requirements (e.g., methods not supported) or when client code needs is checks before calling base members. Unit tests failing when swapping implementations are another sign.
A: New API versions must remain substitutable for clients expecting older behavior. Breaking contract expectations (return types, error codes) violates LSP-like guarantees.
A: Derived classes shouldn’t strengthen preconditions (require extra setup) nor weaken postconditions (provide less result). Keep invariants consistent with the base definition.
A: If derived classes must disable base behavior or add flags to avoid inherited logic, switch to composition/strategies. This keeps contracts honest and avoids LSP pitfalls.
A: Create contract tests that run against the base interface and execute them for every implementation. If any fail, the type isn’t substitutable.
A: Yes—ensure generic constraints and variance don't allow incompatible substitutions that break runtime behavior (e.g., returning base types where derived specifics are expected).
A: Derived classes should not introduce new unchecked exceptions for operations the base class promised to handle. Keep the failure semantics consistent.
A: When polymorphic types are serialized, derived classes must adhere to expected schema so consumers can deserialize using the base contract without surprises.
A: Methods in base types throwing NotImplementedException in derived classes, or switch statements on type codes. These indicate the hierarchy is mis-modeled.
A: Without LSP, OCP fails—extensions break existing clients. Ensuring substitutability keeps abstractions reliable and enables safe extension.