Alrighty, so this will be a fun ride here we're talking about the Liskov Substitution principle.
You can find that here at the top.
That's the L in solid.
So again this is best explained by example.
The official documentation says that the Liskov substitution principle is here to help us achieve that.
We can replace our derived classes with base classes without causing errors.
And maybe now you understand why I say that this is best explained by example, because that definition
is hard to understand without seeing actual code.
So I came up with an example here.
And it's about birds and penguins, which cannot fly.
So here we got a class program a static void main.
Then we have a bird.
It's a sparrow.
Then we have a bird.
It's a penguin.
And as you can see, both of them are birds.
You can take a look here.
We got our classes.
Class bird public virtual void fly so every bird can fly.
And then we have our penguin here.
And our penguin is a bird.
That is true.
So we should be able to inherit it.
But the problem is that the penguin cannot fly.
And if we now start our application, we are able to see that we will run into an exception because
we are trying to let our penguin fly.
You can see this here penguin fly, but that does not work.
Penguins cannot fly, but the list goes.
Substitution principle says that we should be able to replace our derived classes with our base classes.
So we should be able to say that a penguin is a bird, but we should not be able to call the fly method
on a penguin because he has no ability to fly.
So how would we solve this problem?
How could we just say that a penguin is a bird?
But the penguin has no option to fly because we never want to provide the the fly method here on the
penguin, just for another developer to call fly on a penguin to cause an exception, that would be
really bad practice.
So let's see how we can solve this problem.
The first thing to do here is to understand that there is a separation between birds.
Some birds can fly and other birds like the penguin cannot fly.
So the ideal way here would be to create a public interface called I flyable.
So any bird that can fly will implement that interface.
Let's just add a method here.
Call it void fly.
Next up, let's go to our public class bird here.
And let's remove the fly method.
Because we have discovered not every bird can fly.
So it has to get removed here.
Now we have public class bird, but every bird can make a sound.
So let's add a public virtual void Makesound for example.
Right.
And that simply prints out the console log chirp whatever sound a bird makes.
So every bird makes a sound.
So we simply add this here.
You know, I'm just doing that for the sake of this video, but that makes it more obvious.
Now here we want to create a sparrow.
A sparrow can fly.
Let's create the public class sparrow and let's inherit it from our bird.
Because a sparrow is a bird and can make a sound.
And in addition to this, it can also fly.
So let's implement our interface here.
Add a comma and then I flyable.
In that way we have created a contract and now need to implement a fly method public void fly.
And again add a simple Console.WriteLine flying.
There we go.
Here at the top we have our bird Sparrow new bird.
Let's replace it with a sparrow and new sparrow.
Then we can call fly right.
But we should be able.
And that's the Liskov substitution principle to use our base classes here.
So let's write down bird bird sparrow new sparrow.
But we're not able to call fly.
We are able to call Makesound.
Now in order to call fly we simply cast it.
There we go Sparrow.
Now wrap around here and I fly able interface.
In that way we make sure that Sparrow is using the I flyable interface.
Right.
And then we can call fly.
All right.
So to sum it up, we cast our sparrow here to be or to implement I flyable so we can call fly on a bird
base class.
That's the important part I know that one is kind of tricky right.
So next up we have our penguin here which is still a bird.
And here we got our fly.
But.
Fly doesn't exist anymore.
We do not have to override it because it's just not there anymore.
Instead, we have our make sound.
So public override void.
There you go.
Make sound.
And we could basically just call make sound here or add anything else.
That doesn't really matter.
If the penguin makes a different sound, that will be totally fine.
And here you can now see that we are not even able to call the fly method, which is totally correct.
A penguin cannot fly, so we should have no access to any kind of fly method, right?
So what we can do instead is we can take the penguin and make a sound here.
Totally fine.
Still.
And that's the important part we can call the fly method on a bird.
So imagine how crazy that basically is.
We have two birds, the base class already.
I'm talking about the base class here Sparrow bird and penguin bird.
And on the one we can call the fly method, but on the other we can't.
And this is because we use an an interface.
Then we casting our base class.
So the sparrow bird here to the I flyable interface and then call the fly method.
In that way we were able to achieve that.
Our derived classes does not break anything when we use it like a base class.
This is what we're doing here.
I know that this is definitely way more advanced, but that's the Liskov substitution principle.
And whenever you use inheritance, chances are that you run in exactly such a situation where you have
two classes, two, uh, derived classes, for example, and one of them has a functionality that shouldn't
be there, but you need that functionality on the other class.
Then you can simply create an interface, cast it just like that, and make sure that you follow along
with the LSP.
The Liskov substitution principle.