Liskov Substitution Principle (LSP)


In this article we are going to discuss Liskov Substitution Principle (LSP) in C#. There are many blogs, articles are available on the internet regarding Liskov Substitution Principle (LSP) but in this particular article I will try to explain to you with as much as simple.

Liskov Substitution Principle (LSP)

The Liskov Substitution Principle (LSP) is one of the five SOLID principles of object-oriented design. It states that objects of a superclass should be replaceable with objects of a subclass without affecting the correctness of the program.

In other words, if S is a subtype of T, then objects of type T may be replaced with objects of type S without altering any of the desirable properties of the program (correctness, task performed, etc.).

To understand the Liskov Substitution Principle with an example in C# and how it can be violated, let's look at the following scenario:

Here’s a detailed explanation along with an example of violating and adhering to the OCP in C#:

Liskov Substitution Principal in C#

Violating Liskov Substitution Principle (LSP)

In this example, the Penguin class violates the Liskov Substitution Principle. According to the principle, a Penguin should be replaceable with any Bird without affecting the program's correctness. However, since Penguin cannot fly, calling the Fly method throws an exception, which violates the expectations set by the Bird class.

using System;
public abstract class Bird
{
    public abstract void Fly();
}

public class Parrot: Bird
{
    public override void Fly()
    {
        Console.WriteLine("Parrot is flying");
    }
}

public class Penguin : Bird
{
    public override void Fly()
    {
        throw new NotSupportedException("Penguins can't fly");
    }
}

public class Program
{
    public static void Main()
    {
        Bird bird1 = new Parrot();
        bird1.Fly(); // This works correctly

        Bird bird2 = new Penguin();
        try
        {
            bird2.Fly(); // This throws an exception
        }
        catch (NotSupportedException ex)
        {
            Console.WriteLine(ex.Message); // Output: Penguins can't fly
        }
    }
}

Output

Liskov Substitution Principal in C#

In this example:

  • We have a base class Bird with a method Fly.
  • Parrot is a subclass of Bird and it correctly overrides the Fly method.
  • Penguin is also a subclass of Bird, but it cannot fly, so overriding the Fly method to throw an exception violates the LSP.

Correcting the Violation

To correct the violation, we redesign the classes to ensure that subclasses only implement methods they can logically support.

using System;

public interface IBird
{
    void MakeSound();
}

public interface IFlyingBird : IBird
{
    void Fly();
}

public class Parrot: IFlyingBird
{
    public void Fly()
    {
        Console.WriteLine("Parrot is flying");
    }

    public void MakeSound()
    {
        Console.WriteLine("Parrot is makevoice");
    }
}

public class Penguin : IBird
{
    public void MakeSound()
    {
        Console.WriteLine("Penguin is squawking");
    }
}

public class Program
{
    public static void Main()
    {
        IFlyingBird flyingBird = new Parrot();
        flyingBird.Fly(); // This works correctly
        flyingBird.MakeSound(); // This works correctly

        IBird bird = new Penguin();
        bird.MakeSound(); // This works correctly
        // No Fly method to call, so no incorrect expectations
    }
}

Output

Liskov Substitution Principal in C#
Prev Next