In the previous post I discussed the Open Close Principle, today I will talk about Liskov Substitution Principle. Liskov Substitution Principle is the third principle in SOLID principles. Original definition of Liskov Substitution Principle states that
Let q(x) be a property provable about objects x of type T. Then q(y) should be provable for objects y of type S, where S is a subtype of T.
This definition is not very clear to understand, later Robert C. Martin (Uncle Bob) stated LSP as
Subtypes must be substitutable for their base types
So all Liskov Substitution Principle is saying is that a subtype must be replaceable by its parent type. Let’s take a look at a code example to understand this further.
public interface IDuck { void Quack(); void Fly(); } public class MallardDuck : IDuck { public void Quack() { Console.WriteLine("I quack like a Mallard duck."); } public void Fly() { Console.WriteLine("I fly like a Mallard duck."); } } public class BaliDuck : IDuck { public void Quack() { Console.WriteLine("I quack like a Bali duck."); } public void Fly() { Console.WriteLine("I fly like a Bali duck."); } }
In the above code you can see that I have one interface IDuck which has two methods Quack & Fly. We have created two classes MallardDuck and BaliDuck that both implement this interface. They both have their own implementation of Quack and Fly methods.
What happens if we have to introduce a new class RubberDuck? We will have to implement the IDuck interface, and both its Quack and Fly methods, but RubberDuck does not depict the fly behavior. In this case we might be tempted to leave the Fly method empty.
public class RubberDuck : IDuck { public void Quack() { Console.WriteLine("I quack like a Rubber duck."); } public void Fly() { throw new NotImplementedException(); } }
Now our code is violating the Liskov Substitution Principle, because RubberDuck cannot be replaced by IDuck interface as it does not depict the same behavior. We can solve this problem by breaking the IDuck interface into two separate interfaces IFly and IQuack. Then our MallardDuck and BaliDuck classes can implement both of these interfaces while the RubberDuck class will only implement the IQuack interface. Let’s take a look at this code
public interface IFly { void Fly(); } public interface IQuack { void Quack(); } public class MallardDuck : IFly, IQuack { public void Quack() { Console.WriteLine("I quack like a Mallard duck."); } public void Fly() { Console.WriteLine("I fly like a Mallard duck."); } } public class BaliDuck : IFly, IQuack { public void Quack() { Console.WriteLine("I quack like a Bali duck."); } public void Fly() { Console.WriteLine("I fly like a Bali duck."); } } public class RubberDuck : IQuack { public void Quack() { Console.WriteLine("I quack like a Rubber duck."); } }
You can see now RubberDuck class is now only implementing the IQuack interface so everywhere we want to use RubberDuck class we can use IQuack interface abstraction and our code will not break. Thus we are not violating the Liskov Substitution Principle anymore!
In the upcoming article, we will look into Interface Segregation Principle, they fourth in the list of SOLID Principles, till then happy coding!
can you please explain Liskov Substitution Principle? I can see that your example is of ISP and not LSP.
Hi Aditya,
Many times when you violate one of the SOLID principle, you will find that you will also be violating some other. In this case because I used an Interface to explain LSP you felt that I am violating ISP.
To understand LSP more think of the following design:
You have to design a blog engine where Users can create posts, edit them and post some comments on them. One possible way to implement this can be by creating a base User class which has the following methods
Now imagine that there are two classes Admin and Moderator that are inheriting from this user class. Up till now this is fine. Later down the road you realise that guest users can also comment on post therefore you create a Guest class and inherit from the User class. In this case a guest cannot create a post or edit a post therefore it is not 100% substitute able for the User class. This design will violate LSP now and should be fixed.
I hope I was able to clear your confusion but feel free to let me know if you need further explanation.
No he (Aditya) did not say you violated ISP he said you explained ISP as the LSP example which I agree with. In your example above all you did was apply the ISP principle by separating it into two interfaces. How is that describing LSP which basically states I should be able to do var Rectangle = new Square and there should be no side effects or problems. You did not explain LSP.