DEV Community

Cover image for SOLID: The Liskov Substitution Principle (LSP) in C#
Aditya Sharma
Aditya Sharma

Posted on

SOLID: The Liskov Substitution Principle (LSP) in C#

Introduction

The Liskov Substitution Principle (LSP) is a foundational concept in object-oriented design, introduced by Barbara Liskov in 1987. It states:

"Objects of a superclass should be replaceable with objects of a subclass without affecting the correctness of the program."

In simpler terms, derived classes must be substitutable for their base classes without altering the expected behavior of the program. LSP ensures that a class hierarchy is designed in a way that promotes reusability and reliability.

Inheritance

Key Aspects of LSP

  1. Behavioral Consistency: Subclasses must adhere to the behavior defined by their base classes.
  2. No Surprises: A subclass should not override or weaken any functionality of the base class.
  3. Contracts: Subclasses should honor the "contract" (e.g., preconditions and postconditions) established by the base class.

Violating LSP often leads to fragile code that is hard to maintain or extend.

Bad code ❌

public class Rectangle
{
    public virtual double Width { get; set; }
    public virtual double Height { get; set; }

    public double GetArea() => Width * Height;
}

public class Square : Rectangle
{
    public override double Width
    {
        set { base.Width = base.Height = value; }
    }

    public override double Height
    {
        set { base.Width = base.Height = value; }
    }
}

public class LSPViolationDemo
{
    public static void Main()
    {
        Rectangle rectangle = new Square(); // Substitution occurs here
        rectangle.Width = 4;
        rectangle.Height = 5; // Expecting Width=4 and Height=5 for a rectangle

        Console.WriteLine($"Area: {rectangle.GetArea()}"); // Output: 25, not 20!
    }
}
Enter fullscreen mode Exit fullscreen mode

What's Wrong? ❌

Substituting Square for Rectangle violates expectations. A rectangle can have different widths and heights, but a square enforces equal sides. The GetArea result is incorrect in this context.

Adhering to LSP: A Better Design ✔

Inheritance

To adhere to LSP, avoid forcing subclasses into incompatible behaviors. In this case, separating Rectangle and Square into distinct hierarchies solves the issue:

public abstract class Shape
{
    public abstract double GetArea();
}

public class Rectangle : Shape
{
    public double Width { get; set; }
    public double Height { get; set; }

    public override double GetArea() => Width * Height;
}

public class Square : Shape
{
    public double SideLength { get; set; }

    public override double GetArea() => SideLength * SideLength;
}

public class LSPAdherenceDemo
{
    public static void Main()
    {
        Shape rectangle = new Rectangle { Width = 4, Height = 5 };
        Shape square = new Square { SideLength = 4 };

        Console.WriteLine($"Rectangle Area: {rectangle.GetArea()}");
        Console.WriteLine($"Square Area: {square.GetArea()}");
    }
}
Enter fullscreen mode Exit fullscreen mode

Why This Works?

  1. Both Rectangle and Square derive from Shape, but they operate independently, adhering to their specific behaviors.
  2. LSP is preserved because the substitution respects each class's expected behavior.

Benefits of Following LSP

  1. Improved Reusability: Subclasses work seamlessly with existing code.
  2. Ease of Testing: Code that adheres to LSP is predictable and easier to test.
  3. Enhanced Maintenance: Clear boundaries between classes make debugging and extending functionality straightforward.

Conclusion

The Liskov Substitution Principle is critical for creating robust and flexible object-oriented designs. By ensuring that subclasses can be used interchangeably with their base classes without causing unexpected behavior, you build systems that are easier to maintain and extend. When designing your class hierarchies, always ask: "Can this subclass replace its base class without altering the program's behavior?"

Following LSP not only strengthens your adherence to SOLID principles but also sets the foundation for scalable and maintainable software solutions. Happy coding!

dance

Let connect on LinkedIn and checkout my GitHub repos:

Top comments (0)