Liskov Substitution Principle

What is the Liskov Substition Principle (LSP)? Well, honestly, it’s a lot of things and I’m not going to try and explain them all in this article because I’m not even sure I understand it all lol. However, the one part of it I get is this:

If T is S, then S should be swappable¬†with T. Now that’s a paraphrase of an even more awkward explanation, and I’ve heard other people interpret this differently, but I’m going to discuss what it means to me. To me it simply means that you have be careful when sub-classing. When you override functionality, you have to make sure that you aren’t changing anything that would affect the object’s behavior if we thought this object was actually its parent. Here’s what I mean:

If a Square is a Rectangle, then why is it a Square? Well the difference is that a Square is simply a Rectangle that has equal width as it does length. Ok great. So can I change the width of the Square? Sure, but if you only changed the width and not the length, then it would be a Rectangle again, not a square. So as developers, we start thinking in terms of constraints. If we’re going to go through the hassle of making a Square class, then we’re going to ensure that it’s always playing by Square rules! Consider the following:

This rectangle is pretty basic. It has getters and setters for the length and width, and a function that calculates its area. So now let’s create that subclass:

Here’s we enforce our square rules (that length and width are always equal) by overriding the auto properties. Cool. Now any time we change the length, the width is updated to match and vice versa. And now since it inherits from Rectangle, we should be able to use our square anywhere that we are asked for a rectangle. Let’s see what happens:

Here we create a method that accepts a rectangle. And since it takes a rectangle, we as developers would assume that Rectangle rules apply. As such, we attempt to grow one side by 5 and then calculate the area. But what happens? Well, polymorphism takes over, and the property override on the Width property sets the Length to the same value. This method didn’t know any better because it was working with a rectangle, not a square. So now we’re outputting inaccurate data!

This is a textbook example of a LSP violation. Can you find another? How do we fix this? Simple, square shouldn’t inherit from rectangle because it is not a rectangle in that it has a different set of rules. But why not fix the implementation of the Square’s length and width property? Well… then how do you know that the square object is in a valid state? Why bother if you can’t enforce it?

Here is a fiddle showing this example in motion: https://dotnetfiddle.net/hxPntB

Leave a Reply