LSP (Liskov Substitution Principle) is a fundamental principle of OOP and states that derived classes should be able to extend their base classes without changing their behavior. In other words, derived classes should be replaceable for their base types, i.e., a reference to a base class should be replaceable with a derived class without affecting the behavior. The Liskov Substitution Principle represents a strong behavioral subtyping and was introduced by Barbara Liskov in the year 1987.
According to Barbara Liskov, "What is wanted here is something like the following substitution property: If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2 then S is a subtype of T."
A classic example of violation of the Liskov Substitution Principle is the Rectangle - Square problem. The Square class extends the Rectangle class and assumes that the width and height are equal.
Consider the following class. The Rectangle class contains two data members -- width and height. There are also three properties -- Height, Width, and Area. While the first two properties set the height and the width of the rectangle, the Area property has a getter that returns the area of the rectangle.
class Rectangle
{
protected int width;
protected int height;
public virtual int Width
{
get
{
return width;
}
set
{
width = value;
}
}
public virtual int Height
{
get
{
return height;
}
set
{
height = value;
}
}
public int Area
{
get
{
return height * width;
}
}
}
A Square is a type of rectangle all of whose sides are of equal size, i.e., the width and height of a Square is the same.
class Square : Rectangle
{
public override int Width
{
get
{
return width;
}
set
{
width = value;
height = value;
}
}
public override int Height
{
get
{
return width;
}
set
{
width = value;
height = value;
}
}
}
Consider another class called ObjectFactory.
class ObjectFactory
{
public static Rectangle GetRectangleInstance()
{
return new Square();
}
}
Note that the setters for the Width and Height properties in the Square class have been overridden and modified to ensure that the height and width are the same. Let's now create an instance of the Rectangle class using and set its height and width properties.
Rectangle s = ObjectFactory.GetRectangleInstance(); s.Height = 9;s.Width = 8;Console.WriteLine(s.Area);
The above code snippet when executed would display the value 64 in the console. The expected value is 72 since the width and height mentioned is 9 and 8 respectively. This is a violation of the Liskov Substitution Principle. This is because the Square class that has extended the Rectangle class has modified the behavior. To ensure that the Liskov Substitution Principle is not violated, the Square class can extend the Rectangle class but shouldn't modify the behavior. The behavior has been changed by modifying the setters for both the properties Width and Height. The values of height and width are same if it is a Square -- they shouldn't be the same if it is a Rectangle.
How do we fix this, i.e., ensure that this principle is not violated Well, you can have a new class introduced called Quadrilateral and ensure that both the Rectangle and the Square classes extend the Quadrilateral class.
public class Quadrilateral
{
public virtual int Height { get; set; }
public virtual int Width { get; set; }
public int Area
{
get
{
return Height * Width;
}
}
}
Now, both the Rectangle and Square classes should extend the Quadrilateral class and set the values of the Width and Height properties appropriately. In essence, the derived classes should have the necessary functionality to set values to these properties based on the type of the Quadrilateral instance you need to calculate area for. Note that both the Height and Width properties have been marked as virtual in the Quadrilateral class meaning that these properties should be overridden by the classes that derive the Quadrilateral class.
Liskov Substitution Principle is an extension of the Open Close Principle and is violated when you have written code that throws "not implemented exceptions" or you hide methods in a derived class that have been marked as virtual in the base class. If your code adheres to the Liskov Substitution Principle you have many benefits. These include: code re-usability, reduced coupling, and easier maintenance.