12,817,112 members (34,971 online)
alternative version

#### Stats

42.3K views
36 bookmarked
Posted 24 May 2013

# Liskov Substitution

, 26 May 2013 CPOL
 Rate this:
A view on the principle including possible solutions to the Rectangle/Square example, also explaining why it is not a good example for real programming situations and presenting a more realistic example that you may find while programming.

## Introduction

I am seeing a lot of articles talking about SOLID principles lately and one that is really getting my attention, because of the bad examples, is Liskov Substitution (also known as LSP).

The principle states that instances of a parent type should be replaceable by instances of sub-types without changing the correctess of the application. This can initially look silly as any sub-type instance can be given as the parameter to a method that receives instances of a parent type, but then there is the example intended to show how the principle gets violated: Rectangles and Squares.

Considering that inheritance means an is a relationship, the example says: A `Square` is a `Rectangle` (and so, it is a sub-class of `Rectangle`) and then it shows how we are breaking the Liskov Substitution, as for a `Rectangle` it is possible to change `Width` and `Height` independently, which is not valid for a `Square`.

Most solutions I've seen lately say to create a base class, so both `Rectangle` and `Square` inherit from it, but they keep the independent `Width` and `Height` modifiable properties.

That's not a solution at all. First, the `Square` stops being a `Rectangle` and then the base class continues to have independent `Width` and `Height` properties, so such "solution" is only adding complexity (a new base type), is breaking the is a relationship between `Square` and `Rectangle` (and so it does not seem logical if compared to geometry) and keeps the problem that a `Square` shouldn't have independent `Width` and `Height` properties.

In fact, I think that the entire `Rectangle` and `Square` example is not meant to show a solution, only to show the problem.

## Other explanations to the Liskov Substitution

There are other explanations to the Liskov Substitution. Instead of saying that instances of a parent type should be replaceable by instances of sub-types without changing the correctness of the application, it is said that a sub-type can't change the behavior of a parent type.

Well, I should say that even if I do understand the idea of such sentence, it is not precise. Surely we can't change an expected logic (as this will make the program function incorrectly) but sub-types, in special method overriding, exist exactly to change a behavior. If we can't change a behavior at all it is like saying we can only inherit to add new properties or methods, but that we can never override any method, which is clearly not the purpose.

But there is another explanation to the Liskov Substitution principle, which I consider better. Such explanation says that a sub-type should never have stronger pre-conditions or weaker post-conditions than the base type. OK, maybe the terms pre-conditions and post-conditions are not the easiest ones, but it means something like this:

• If a method on the base type accepted `null` values as input, an overriden method on a sub-type should also accept `null` values. It can't simply start to throw exceptions saying `null` parameters are invalid as this will be a stronger pre-condition;
• On the opposite side, if a base type method gave a guarantee that it will never return `null`, a sub-type can't override such method and return `null`. After all, anyone receiving the instance cast as the parent type considers that `null` will never be returned. An inheritor type still is a kind of the parent type, so it must still guarantee the same post-conditions (such post-condition is very common when returning collections, as in many situations it is expected to receive an empty collection instead of `null`).

## How the Rectangle and Square relate to the Principle?

The `Rectangle` gives us two properties, `Width` and `Height` and, if nothing else is told, we can assume they are independent properties. Here we can say that we don't have an explicitly told guarantee but by simple logic we can imagine that a property like `TotalArea` is calculated on something else (like `Width` and `Height`) but `Width` and `Height` seem completely independent of each other. Something that's not told, though, is if negative values are supported as in the real world there aren't negative sizes (even the absolute zero is invalid, as that means the object does not exist) and in the programming world those values are valid, but that's not the focus at this moment.

In my opinion the Rectangle/Square situation is a "real world" comparison that simply fails in the OOP world. In special, the "is a" expression causes the problem. A `Square` is a `Rectangle` in geometry. Such is a expression is used to say that a sub-class is a kind of a base-class. But what happens if we have these two methods: `DrawRectangle(Rectangle)` and `DrawSquare(Square)` in static typed languages (C# is mainly a static typed language)?

Considering a `Square` is a `Rectangle` we can call `DrawRectangle()` giving a `Square` object as parameter and this one works really fine in static typed OOP languages.

But now I ask you: Is a `Rectangle` that has equally sized `Width` and `Height` a `Square`?

If your answer is Yes (in geometry, it is) then a `DrawSquare()` could be called with a `Rectangle` instance where `Width` and `Height` are equal. But in a static typed language, that's not true. An instance of the `Rectangle` type is only a `Rectangle`, not a `Square`, even if it shares all the same properties of a `Square` (that is, equally sized `Width` and `Height`).

### So, how do we solve the problem?

My personal solution to this problem is: There should be only a `Rectangle` type. A `Square` is simply an observation of the properties of such `Rectangle`. So, a property like `IsSquare` will solve the problems if we compare it to how it works in Geometry.

That is: You can create a `Rectangle` of `Width` 100 and `Height` 200. The `IsSquare` will return `false`. Then, you can change `Width` to be 200 too. If you get the value of `IsSquare`, it will be `true`. Great!

So the code can be:

```public class Rectangle
{
public int Width { get; set; }
public int Height { get; set; }

public bool IsSquare
{
get
{
return Width == Height;
}
}
}
```

But, by having only a `Rectangle` type we can't create a method like `DrawSquare(Square)`.

So, if we do a `DrawSquare(Rectangle)` that should only accept `Square` instances, it will still accept any `Rectangle` at compile-time. Of course we can validate that at run-time (the same way we can validate if any parameter is `null` at run-time), but what if we want to solve the problem of a `Square` and a `Rectangle` using real types?

### Solving the Problem with Real Types

Before presenting the possible solutions I want to let it clear that I consider those solutions bad design. As just explained, any rectangle that has equally sized width and height is a square, so I consider it much simpler and logical to have a property that can tell if the actual `Rectangle` is also a `Square` or not.

But in static typed languages we may want to give a compile-time guarantee that a parameter will have certain traits (in this case, that it will be a `Square` instead of being any kind of `Rectangle`). Even If I do know OOP situations that require similar compile-time constraints I can't really say that I see real situations for the Rectangle/Square situation, especially because this seems to be a comparison to the real world geometry and, in the real world, we will never ask for a `Square` instead of a `Rectangle` without putting other requirements. For example: If we need to buy ceramic tiles to replace broken ones we will ask for ceramic tiles of a specific size (for example, 10x10cm) with a specific color or texture. Yes, it is a square if the size is 10x10 but if I receive a tile with the same color/texture, square but with a size of 12x12 it will not work, so in the real world situation we will validate the sizes even if the received object is a square. If we are already validating the right sizes, validating that it is a square is redundant.

But if we should make it work in OOP (maybe to say that we have found a solution and really understood the problem), OK, let's do it. But first, answer two questions:

1. Do the types (`Square` and `Rectangle`) need to be modifiable?
2. Is every `Rectangle` with equally sized `Width` and `Height` a real `Square`?

And accordingly to the answers things can be solved differently (or they can't be solved at all).

So, for each combination of the answers we have:

• Need to be modifiable? No. Is a Rectangle(100, 100) a Square? No

This is the easiest one: Make the types immutable (that is, properties are only set at creation time). The `Square` inherits from the `Rectangle` and in its constructor it only receives a single parameter which it will use to fill both `Width` and `Height` of the `Rectangle` type. As you can't change the `Width` or `Height` later, everything will be OK, but if you do a `new Rectangle(100, 100)` instead of `new Square(100)` you will have a `Rectangle` that could be a `Square`, but it will not have the real `Square` type, so it will not be working exactly like in geometry.

So, the code could be:

```public class Rectangle
{
public Rectangle(int width, int height)
{
// Validating if width and height are > 0
// is optional for this example, but the
// real classes will probably do such verification.

Width = width;
Height = height;
}

public int Width { get; private set; }
public int Height { get; private set; }
}
public class Square:
Rectangle
{
public Square(int size):
base(size, size)
{
}
}
```

With this case, the following situations work work:

```DrawRectangle(new Rectangle(100, 200));
DrawRectangle(new Square(100));
DrawSquare(new Square(100));
```

While this will return false:

```Rectangle rectangle = new Rectangle(100, 100);
return rectangle is Square;
```
• Need to be modifiable? No. Is a Rectangle(100, 100) a Square? Yes

We still need the types to be immutable but we can't make the constructor of the `Rectangle` type public. We should always use a constructor method capable of creating a `Rectangle` (if sizes are different) or a `Square` (if sizes are equal). The constructor of a `Square` can be public as it does not risk of being of another type. But note that even if the `Rectangle.Create(100, 100)` returns an instance of a `Square`, it will be returned as a `Rectangle`, so a cast to `Square` will be needed if you want to pass such `Square` to a method that statically requires a `Square` (in this case, I really consider a property `IsSquare` much cleaner than a cast).

So, the code could be:

```public class Rectangle
{
public static Rectangle Create(int width, int height)
{
if (width == height)
return new Square(width);

return new Rectangle(width, height);
}
internal Rectangle(int width, int height)
{
Width = width;
Height = height;
}

public int Width { get; private set; }
public int Height { get; private set; }
}
public class Square:
Rectangle
{
public Square(int size):
base(size, size)
{
}
}
```

And then all the following will work correctly:

```DrawRectangle(Rectangle.Create(100, 200));
DrawRectangle(new Square(100));
DrawSquare(new Square(100));

Rectangle rectangle = Rectangle.Create(100, 100);
bool isSquare = rectangle is Square; // this will be true.

// But if we want to to use this "square" as a real Square
// we will need to cast it.
DrawSquare((Square)rectangle);
```
• Need to be modifiable? Yes. Is a Rectangle(100, 100) a Square? Yes

Impossible because if the user tries to create a `Rectangle` of `Width` and `Height` 100 he will receive a `Square`, then we will introduce a bug as the user will not be able to change the properties individually even if he asked to create a `Rectangle`, not a `Square`. Also, if he creates a `Rectangle` of `Width` 200 and `Height` 100 and later resizes the `Width` to 100 the object will continue to be of type `Rectangle`, as the static type can't change after the object is created.

• Need to be modifiable? Yes. Is a Rectangle(100, 100) a Square? No

In this case we need to make the `Rectangle` type somehow aware that it may be a `Square` (this is already a violation of the Single Responsibility Principle but not of the Liskov Substitution principle). A possible solution is to have a `TrySetWidthAndHeight()` method instead of letting both properties to be modified independently. This way, when we have a `Square` of `Width` and `Height` 100 we can replace both values to 200 in a single step, so there is no risk of receiving an exception when changing `Width` only (as in such case the setter does not know if you will change `Height` or not) and there is no risk of `Height` being changed when it was not requested to change. We can say that a `TrySetWidthAndHeight()` solves the problem without breaking the Liskov Substitution because:

• If we set equally sized `Width` and `Height` values when resizing the Square can validate that it will continue to be a valid square before changing both properties (so it will never be corrupted) and;
• As the method is a `Try` method it lets clear that changing the value may fail. Some people may still argue that there is a change in behavior but as already explained, a change in behavior is valid as long as the pre-conditions are not stronger or the post-conditions weaker. It is always clear that a call to `TrySetWidthAndHeight()` may fail so, if it fails, well, it is expected.

So, the code could be:

```public class Rectangle
{
public int Width { get; protected set; }
public int Height { get; protected set; }

public virtual bool TrySetWidthAndHeight(int width, int height)
{
Width = width;
Height = height;
return true;
}
}
public class Square:
Rectangle
{
public override bool TrySetWidthAndHeight(int width, int height)
{
if (width != height)
return false;

Width = width;
Height = height;
return true;
}
}
```

Note: There are other possible examples, like making a `Rectangle` and a `Square` completely independent or making the `Square` the base class (with only a `Size` property) and then making the `Rectangle` to be a "more versatile" `Square`, but in such cases we will not keep the idea that a `Square` is a `Rectangle` so, even if it solves the programming problem, I don't think it is worth showing them as the whole idea is to make the classes represent the geometry shapes.

## A more realistic example

I hope that my explanation up to this point is clear enough. I really hope that I am helping anyone that was lost with those Rectangle/Square examples to get a real idea of the problem and of the possible solutions, yet I think the entire situation is far from the problems we will find in real applications, so I think it is better to show an example that happens when actually programming applications.

So, let's see a real programming example that happens a lot: Controls that allow multiple child controls.

In WPF the built-in controls that allow that are the layout controls. They don't have any special visual but they are responsible for presenting other controls in different manners. A `Grid` allows you to specify rows and columns sizes (be them fixed sizes, proportional sizes or "best fit" sizes), `StackPanel`s simply put one control after the other, either horizontally or vertically and the `Canvas` allow you to specify the exact position of each control.

These 3 layout controls are based on the `Panel` class. You can see that they behave completely different from each other and if you stick to the idea that a sub-class can't change the behavior of a parent class you may think that's a violation of the principle, but I should say that it is not. The `Panel` class does not give any guarantee on how items will be presented on the screen, which dependency properties must be filled for the controls to be correctly placed or the like. The only guarantee a panel gives is that it can contain multiple controls. We can say that if a method expects a `Panel` as a parameter, instead of expecting one of the more specific classes, that it expects only to access the already existing controls (for example, counting the controls, searching for a sub-control with a specific name or similar) and not that it is expecting to add a new control to be correctly visualized, as the `Panel` simply doesn't give a guarantee that the control will be presented at all.

But what if I decide to create a new control, based on a `Grid` or on a `Canvas` and I simply implement my own logic to display the inner controls, completely ignoring the already existing logic?

This happens a lot when developers don't understand the hierarchy of controls correctly, so instead of inheriting directly from the `Panel` class they inherit from the `Grid` or `Canvas` class (I don't know why but these two are the most common base class that I see people using when generating their layout controls) and completely replace the layout logic.

So, a method expecting to fill a `Canvas` (adding many controls and setting their `Canvas.Left` and `Canvas.Top` properties) can receive an instance of that alternative layout control and the entire generated layout will be completely wrong because the child controls will not be positioned using the `Canvas.Left` and `Canvas.Top` properties (and may even be positioned using another attached Property that was simply not set).

That's a real violation of the Liskov Substitution. In this situation, we solve it by simply inheriting directly from `Panel`. So any method requiring a `Canvas` will not accept such new layout control (that's fine), any method that is only interested in listing the inner controls can still receive the new layout control as a `Panel` (that's fine too) and if there is a method that simply adds new childs to any `Panel`, well, we can say that such method must be corrected to expect a "minimum guarantee" (that is: a guarantee that all controls will be made visible accordingly by only adding them without setting any extra properties [that's the case of the `StackPanel`]).

## Conclusion

My personal conclusion is that the principle is not hard to follow and that many programmers follow it naturally, until they see a bad example of the principle and get lost.

Most of the problems to understand it come from the fact that a sentence ("A Square is a Rectangle") is being translated directly as Square and Rectangle need to be classes and "is a" means inheritance when that's not the case. In fact, it is very rare to translate real life objects to the programming world as there are always mismatches and double interpretations and so it creates a problem in OOP and people usually try to solve the problem keeping the sentence, when to solve the problem it is easier to look at the problem differently and to understand the problem it could be much simpler if real programming situations are presented and correctly solved.

## A Final Note

People who already saw my advanced articles sometimes downvote my basic articles saying that I already did better, that I should talk about advanced topics. Well, I expect a basic article to be judged as a basic article as I am not saying it is an advanced article. The reality is that I write from inspiration and, as I think there is too much material giving false information about this specific topic, I really wanted to talk about it.

## Share

 Engineer Microsoft Corporation United States
I started to program computers when I was 11 years old, as a hobbyist, programming in AMOS Basic and Blitz Basic for Amiga.
At 12 I had my first try with assembler, but it was too difficult at the time. Then, in the same year, I learned C and, after learning C, I was finally able to learn assembler (for Motorola 680x0).
Not sure, but probably between 12 and 13, I started to learn C++. I always programmed "in an object oriented way", but using function pointers instead of virtual methods.

At 15 I started to learn Pascal at school and to use Delphi. At 16 I started my first internship (using Delphi). At 18 I started to work professionally using C++ and since then I've developed my programming skills as a professional developer in C++ and C#, generally creating libraries that help other developers do their work easier, faster and with less errors.

Now I just started working as a Senior Software Engineer at Microsoft.

Take a look at: http://paulozemek.azurewebsites.net/
Or e-mail me at: paulozemek@outlook.com

Codeproject MVP 2012, 2015 & 2016
Microsoft MVP 2013-2014 (now I work at Microsoft so I can't be a Microsoft MVP anymore)

## You may also be interested in...

 First PrevNext
 Excellent phil.o15-Oct-15 8:05 phil.o 15-Oct-15 8:05
 Re: Excellent Paulo Zemek15-Oct-15 8:58 Paulo Zemek 15-Oct-15 8:58
 5 - Fun to read ! Eric Ouellet3-Jul-14 13:08 Eric Ouellet 3-Jul-14 13:08
 Re: 5 - Fun to read ! Paulo Zemek3-Jul-14 13:56 Paulo Zemek 3-Jul-14 13:56
 My vote of 5 cjb11025-Jun-14 21:57 cjb110 25-Jun-14 21:57
 Re: My vote of 5 Paulo Zemek26-Jun-14 3:26 Paulo Zemek 26-Jun-14 3:26
 My vote of 5 M Rayhan8-Nov-13 2:14 M Rayhan 8-Nov-13 2:14
 Re: My vote of 5 Paulo Zemek8-Nov-13 2:51 Paulo Zemek 8-Nov-13 2:51
 Not, "is a", but rather, "has the same interface" Bill_Hallahan6-Nov-13 19:11 Bill_Hallahan 6-Nov-13 19:11
 Re: Not, "is a", but rather, "has the same interface" Paulo Zemek7-Nov-13 3:20 Paulo Zemek 7-Nov-13 3:20
 Re: Not, "is a", but rather, "has the same interface" Bill_Hallahan7-Nov-13 14:05 Bill_Hallahan 7-Nov-13 14:05
 Re: Not, "is a", but rather, "has the same interface" Paulo Zemek7-Nov-13 14:45 Paulo Zemek 7-Nov-13 14:45
 Re: Not, "is a", but rather, "has the same interface" Bill_Hallahan7-Nov-13 15:39 Bill_Hallahan 7-Nov-13 15:39
 Re: Not, "is a", but rather, "has the same interface" Paulo Zemek7-Nov-13 16:21 Paulo Zemek 7-Nov-13 16:21
 Re: Not, "is a", but rather, "has the same interface" Bill_Hallahan7-Nov-13 17:28 Bill_Hallahan 7-Nov-13 17:28
 Re: Not, "is a", but rather, "has the same interface" Paulo Zemek8-Nov-13 10:47 Paulo Zemek 8-Nov-13 10:47
 Re: Not, "is a", but rather, "has the same interface" Bill_Hallahan9-Nov-13 7:19 Bill_Hallahan 9-Nov-13 7:19
 Re: Not, "is a", but rather, "has the same interface" Paulo Zemek9-Nov-13 10:39 Paulo Zemek 9-Nov-13 10:39
 Re: Not, "is a", but rather, "has the same interface" Paulo Zemek9-Nov-13 11:02 Paulo Zemek 9-Nov-13 11:02
 Re: Not, "is a", but rather, "has the same interface" Bill_Hallahan9-Nov-13 13:15 Bill_Hallahan 9-Nov-13 13:15
 Re: Not, "is a", but rather, "has the same interface" Paulo Zemek9-Nov-13 13:28 Paulo Zemek 9-Nov-13 13:28
 Re: Not, "is a", but rather, "has the same interface" yetibrain27-Jan-14 4:31 yetibrain 27-Jan-14 4:31
 Re: Not, "is a", but rather, "has the same interface" Bill_Hallahan30-Jan-14 16:06 Bill_Hallahan 30-Jan-14 16:06
 My vote of 5 Mihai MOGA13-Jun-13 21:41 Mihai MOGA 13-Jun-13 21:41
 Re: My vote of 5 Paulo Zemek14-Jun-13 4:52 Paulo Zemek 14-Jun-13 4:52
 Last Visit: 31-Dec-99 19:00     Last Update: 24-Mar-17 0:24 Refresh 1234 Next »