The third principle from the solid design principles that we're going to take a look out is called the
Liskov Substitution Principle which is named after Bober Liskov.
So the goal of this principle is to specify that subtypes should be immediately substitutable for their
base types.
So once again I know this sounds particularly cryptic so we're going to take a look at an example of
how the Liskov Substitution Principle can actually be broken.
So let's suppose that you define a class called rectangles I'm going to have a class called rectangle
is going to have a bunch of protectin members going to have a bunch of protected members specifically.
We're going to have the width and height of the rectangle.
So we'll have it with height and then I'm going to have a bunch of public members so I might for example
decide to make a constructor which initializes the width and height so that's probably the first thing
I would want.
And also let's make a bunch of getters and setters.
So let's suppose that we decide to generate the getters and setters go into getter and center here or
generate them for both with as well as height.
So we have a bunch of functions which simply allow us to get and set the width and height.
And in addition let's have a function which calculates the area.
So I'm going to have a function which returns an int area and it simply returns with time height.
So this is the area of a particular shape.
Now somewhere down the line I decided I want to extend this model.
But before I do let's suppose I'm using some sort of API for working with rectangle So let's suppose
I have a function called process which takes a rectangle reference x a rectangle reference called R
and let's suppose that it does the following and gets the width of the rectangle.
So our door gets width like so.
It then sets the height of the rectangle with a 10.
So it says our dogs set height to 10 and it expects the area to be equal to width times 10 because well
that's what it's setting right here.
So let's actually output this.
So we expected the area to be with the types 10 but instead we got something different.
What we might get something different in certain situations.
So here we say that's what we got is our dog area.
We have a function for area.
I'm just going to put in and line in here and we can already try this out using an ordinary rectangle
I'm actually going to be fine with just the rectangle So we're going to have a rectangle r with sides
three and four and I'm going to process this rectangle.
Are you actually going to run this and see what the output is now because we're working with a rectangle
and we're clear about the expectations we are clear about the setting of width and height.
The expected area is 30 and we get exactly 30.
There is no problem.
Now let's suppose that somewhere down the line you decide to use inheritance to inherit from rectangle
and make a square.
So this sounds like a good idea.
You make a square which inherits from rectangle and maybe you want to customize it a bit.
So let us suppose for example that we'll have first of all we have the constructor so we'll have a constructor
called Square which takes the size of the square or the size being the length of any of its sizes.
And you initialize the rectangle with size being on both width and height.
Because that's the definition of a square or its width and height is exactly the same.
But in addition let's suppose that you decide to go and make a set with and said height virtual so that
you can override them inside of the square.
So whenever somebody sets the width or the height of the square you set both of the width and the height.
So we go in here and we go into override functions and then we pick a set with as well as set height.
We press OK here and then we totally redefine the way these are implemented.
So essentially here all I'm doing is I'm saying this width is equal to this height is equal to with.
So I'm trying to set both dimensions at the same time and I'm doing the same here with height obviously.
So it looks like a good idea.
Unfortunately what we've just done is we've violated the Liskov Substitution Principle which states
that if you have let's say you have a function which takes a base class any derived class should be
able to be substituted into this function without any problems and unfortunately we're going to have
a problem here because if I make a square let's call it Esky with a side of five and I call process
here.
Something very unpleasant is going to happen now.
Let's just remind ourselves what process does.
So it tries to set the width of the shape or get the width of the shape and store it in a variable.
So we put the width of the shape in the variable.
We said the height to 10 and then we expect the area to be equal to width times 10.
It makes perfect sense but it only makes perfect sense for a rectangle because what we've done in the
square is we've broken the cetra a mechanic we've broken said hide.
Because when you call set height you know just setting the height you're also setting the width.
So now if I execute this you can already guess what's going to happen they will blow up.
It will give us a different expected area.
We couldn't expect in that area of 50.
But we actually get 100 because when we set the height to 10 we also set the width to 10 and so 10 by
10 becomes 100.
And that's what we get here.
So we've broken the Liskov Substitution Principle with this very simple example.
Now one question you might have is well how do we actually fix this.
What can we do about this whole situation in order to avoid this kind of screw up.
Now my argument is that the very idea of inheriting a square from rectangle is not the best idea because
both of them I mean the square.
Yes.
In terms of the language a square is in fact a rectangle so you can say a square should inherit from
rectangle because it is such.
But you could do it differently.
So for example you could just keep rectangles and have a rectangle with a boolean flag indicating whether
it's square or not.
You could also if you want to construct rectangles and squares specifically what you could do is you
could just make a factory and we're going to see factories in just a moment.
But essentially you would have some sort of rectangle factory or something like the following which
would construct the squares or rectangles So you would have a static function which returns a rectangle
and it's going to be called create rectangle with width and height like so.
And you would also have a function which would be called create Square which would also return a rectangle
but it would just take the size of the square to be constructed.
So this is a more sensible way which would break the Liskov Substitution Principle.
So to reiterate the idea is that you should be able to substitute a derived class in this case it's
a class called Squid's derived from Mac Dango.
You should be able to substitute it into any location where a base class is being used like here.
And obviously with our design this is unfortunately possible.