What is the Liskov Substitution Principle and why is it important?

15 minadvancedSOLIDLSPinheritancedesign-principles

Quick Answer

LSP states that objects of a superclass should be replaceable with objects of its subclasses without breaking functionality. Derived classes must honor the contract of the base class. Violations lead to unexpected behavior, bugs, and fragile code. Important for maintaining polymorphism and ensuring reliable inheritance hierarchies.

Detailed Answer

The Liskov Substitution Principle (LSP) is one of the SOLID principles. It states that objects of a derived class should be able to replace objects of the base class without affecting the correctness of the program.

In simple terms: If class B is a subtype of class A, then objects of type A should be replaceable with objects of type B without breaking the application.

Example:

// Violation of LSP
public class Rectangle
{
    public virtual int Width { get; set; }
    public virtual int Height { get; set; }
    
    public int GetArea()
    {
        return Width * Height;
    }
}

public class Square : Rectangle
{
    private int side;
    
    public override int Width
    {
        get { return side; }
        set { side = value; }  // Setting width also affects height
    }
    
    public override int Height
    {
        get { return side; }
        set { side = value; }  // Setting height also affects width
    }
}

// This violates LSP because:
void TestRectangle(Rectangle rect)
{
    rect.Width = 5;
    rect.Height = 4;
    Console.WriteLine(rect.GetArea());  // Expected: 20
    // But if rect is Square, result will be 16 (4*4), breaking expectations
}

// Correct approach following LSP
public abstract class Shape
{
    public abstract int GetArea();
}

public class Rectangle : Shape
{
    public int Width { get; set; }
    public int Height { get; set; }
    
    public override int GetArea()
    {
        return Width * Height;
    }
}

public class Square : Shape
{
    public int Side { get; set; }
    
    public override int GetArea()
    {
        return Side * Side;
    }
}

// Why LSP is important:
// 1. Ensures reliable polymorphism
// 2. Prevents unexpected behavior in derived classes
// 3. Makes code more maintainable and predictable
// 4. Enables proper use of inheritance hierarchies