The Liskov Substitution Principle states that a subtype should be substitutable for that type (without altering the correctness of the program).
- Can someone please provide an example of this principle in the domain of vehicles (automotives)?
- Can someone please provide an example of a violation of this principle in the domain of vehicles?
I've read about the square/rectangle example, but I think that an example with vehicles will give me a better understanding of the concept.
For me, this 1996 Quote from Uncle Bob (Robert C Martin) summarises the LSP best:
In recent times, as an alternative to inheritance abstractions based on sub-classing from a (usually abstract) base/super class, we also often use interfaces for polymorphic abstraction. The LSP has implications to both the consumer, and implementation of the abstraction:
Uncle Bob, and others regard the LSP as a cornerstone of Design by Contract.
Example, LSP Compliance
Here is an example using an interface
IVehiclewhich can have multiple implementations (the interface can be substituted for an abstract base class - same effect).This implementation of a consumer of
IVehiclestays within the bounds of LSP:Glaring Violation - Runtime type switching
Here's an example of a violation of LSP, using RTTI and then Downcasting - Uncle Bob calls this a 'glaring violation':
The violating method goes beyond the contracted
IVehicleinterface and hacks a specific path for a known implementation of the interface (or a subclass, if using inheritance instead of interfaces). Uncle Bob also explains that LSP violations using type-switching behaviour usually also violate the Open Closed principle, since continual modification to the function will be required in order to accomodate new subclasses.Violation - Pre condition is strengthened by a subtype
Another violation example would be where a "pre condition is strengthened by a subtype":
Here, the Scooter subclass attempts to Violate the LSP as it tries to strengthen (further constrain) the precondition on the base class
Drivemethod thatmiles < 300, to now a maximum of less than 50 miles. This is invalid, since by the contract definition ofVehicleallows 300 miles.Similarly, Post Conditions may not be weakened (i.e. relaxed) by a subtype.
(Users of Code Contracts in C# will note that preconditions and postconditions MUST be placed on the interface via a
ContractClassForclass, and cannot be placed within implementation classes, thus avoiding the violation)Subtle Violation - Abuse of an interface implementation by a subclass
A
more subtleviolation (also Uncle Bob's terminology) can be shown with a dubious derived class which implements the interface:Here, irrespective of how far the
ToyCaris driven, the fuel remaining will always be zero, which will be surprising to users of theIVehicleinterface (i.e. infinite MPG consumption - perpetual motion?). In this case, the problem is that despiteToyCarhaving implemented all of the requirements of the interface,ToyCarjust inherently isn't a realIVehicleand just "rubber stamps" the interface.One way to to prevent your interfaces or abstract base classes from being abused in this way is to ensure a good set of Unit Tests are made available on the interface / abstract base class to test that all implementations meet the expectations (and any assumptions). Unit tests are also great at documenting typical usage. e.g. this
NUnit Theorywill rejectToyCarfrom making it into your production code base:Edit, Re: OpenDoor
Opening doors sounds like a different concern entirely, so needs to be separated accordingly (i.e. the "S" and "I" in SOLID), e.g.
In all cases, to avoid violating LSP, code which required objects of these interfaces should not downcast the interface to access extra functionality. The code should select the appropriate minimum interface / (super)class it needs, and stick to just the contracted functionality on that interface.