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!