Liskov Substitution principle

C# Jun 28, 2021
Subtypes must be substitutable for their base types.

Barbara Liskov introduced this principle in 1987. It extends the Open-Closed Principle by focusing on a superclass’s behavior and its subtypes. Its importance becomes evident when we consider the consequences of violating it. Consider an application that uses the following class.

public class Rectangle 
{ 
  private double width;
 
  private double height; 
  public double Width 
  { 
    get 
    { 
      return width; 
    } 
    set 
    { 
      width = value; 
    }
  } 
  public double Height 
  { 
    get 
    { 
      return height; 
    } 
    set 
    { 
      height = value; 
    } 
  } 
}

Imagine that one day, the client demands the ability to manipulate squares in addition to rectangles. Since a square is a rectangle, the square class should be derived from the Rectangle class.

public class Square : Rectangle
{
}

However, by doing that we will encounter two problems:

  • A square does not need both height and width variables inherited from the rectangle, and this could create a significant waste in memory if we have to make hundreds of thousands of square objects.
  • The width and height setter properties inherited from the rectangle are inappropriate for a square since the width and height of a square are identical.

To set both height and width to the same value, we can create two new properties as follows:

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

Now, when someone sets the width of a square object, its height will change accordingly and vice-versa.

Square s = new Square(); 
s.SetWidth(1); // Sets width and height to 1. 
s.SetHeight(2); // sets width and height to 2.

Let’s move forward and consider this other function:

public void A(Rectangle r) 
{ 
  r.SetWidth(32); // calls Rectangle.SetWidth 
}

If we pass a reference to a square object into this function, we will violate the LSP because the function does not work for derivatives of its arguments. The properties width and height aren’t polymorphic because they aren’t declared virtual in rectangle (the square object will be corrupted because the height won’t be changed). However, by declaring the setter properties to be virtual we will face another violation, the OCP. The creation of a derived class square is causing changes to the base class rectangle.

Tags

Great! You've successfully subscribed.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.