I was following this highly voted question on possible violation of Liskov Substitution principle. I know what the Liskov Substitution principle is, but what is still not clear in my mind is what might go wrong if I as a developer do not think about the principle while writing object-oriented code.
|
|
I think it's stated very well in that question which is one of the reasons that was voted so highly.
Imagine if you will:
In this method, occasionally the .Close() call will blow up, so now based on the concrete implementation of a derived type you have to change the way this method behaves from how this method would be written if Task had no subtypes that could be handed to this method. Due to liskov substitution violations, the code that uses your type will have to have explicit knowledge of the internal workings of derived types to treat them differently. This tightly couples code and just generally makes the implementation harder to use consistently. |
|||||||||||
|
|
If you don't fulfill the contract that has been defined in the base class, things can silently fail when you get results that are off.
Should any of these not hold, the caller might get a result he does not expect. |
|||||||||
|
|
In layman's words: Your code will have an awful lot of CASE/switch clauses all over. Every one of those CASE/switch clauses will need new cases added from time to time, meaning the code base is not as scalable and maintainable as it should be. LSP allows code to work more like hardware: You don't have to modify your iPod because you bought a new pair of external speakers, since both the old and the new external speakers respect the same interface, they are interchangeable without the iPod losing desired functionality . |
|||||||||
|
|
to give a real life example with java's UndoManager it inherits from however UndoManager has more states and acts like an undo buffer (each call to this leads to the hypothetical situation where you add a UndoManager to a CompoundEdit before calling I rolled my own |
|||
|
|
|
Consider a classic case from the annals of interview questions: you have derived Circle from Ellipse. Why? Because a circle IS-AN ellipse, of course! Except... ellipse has two functions:
Clearly, these must be re-defined for Circle, because a Circle has a uniform radius. You have two possibilities:
Most OO languages don't support the second, and for a good reason: it would be surprising to find that your Circle is no longer a Circle. So the first option is the best. But consider the following function:
Imagine that some_function calls e.set_alpha_radius. But because e was really a Circle, it surprisingly has its beta radius also set. And herein lies the substitution principle: a subclass must be substitutable for a superclass. Otherwise surprising stuff happens. |
|||||||
|
|
You can also look at it from a modelling point of view. When you say that an instance of class So, violating the LSP means that there is some contradiction in your design: you are defining some categories for your objects and then you are not respecting them in your implementation, something must be wrong. Like making a box with a tag: "This box contains only blue balls", and then throwing a red ball into it. What is the use of such a tag if it shows the wrong information? |
||||
|
|
|
If you want to feel the problem of violating LSP, think what happens if you have only .dll/.jar of base class (no source code) and you have to build new derived class. You can never complete this task. |
|||||
|
