The original definition of the principle is:
"Let q(x) be a property provable about objects x of type T. Then q(y) should be true for objects y of type S where S is a subtype of T."
This is all about substitution. Any place where a base class is used in the code, you should be able to use a subclass as well.
A few implications come from this:
- You shouldn't do runtime checks for which subtype a certain object is. That means it is not automatically extendable and must be modified each time a new subclass is added.
- You shouldn't do less in a derived class. That means for instance that you shouldn't override a certain method and just leave it empty. That's not always a violation of LSP, but it can be.
- You shouldn't hide the implemention of a base class method (by using new). If you have a method that takes in an object of the type of the base class and calls such a method, only the base class method will be called.
- Even if it is often said that IS A means inheritance is the right approach, that not really true. Substitutability should be taken into account.
- You can't simply look at the implementation of the model in isolation and decide if it is correct or not. You need to take into account the natural assumptions users will have about them. KISS still applies though.
- Not following this can bring out some hard to find bugs if someone else believes the implementation follows LSP.