introduce
Liskov Substitution Principle (LSP) is a basic concept of object-oriented design, proposed by Barbara Liskov in 1987.
"Objects of a superclass should be replaceable with objects of a subclass without affecting the correctness of the program."
Simply put, a derived class must be able to replace its base class without changing the intended behavior of the program. LSP ensures that class hierarchies are designed in a way that improves reusability and reliability.
Key aspects of LSP
- Behavioral consistency: Subclasses must adhere to the behavior defined by their base class.
- No surprises: Subcategories should not override or diminish any functionality of the base category.
- contract: Subclasses should adhere to the “contracts” (such as preconditions and postconditions) established by the base class.
Violating LSP often results in brittle code that is difficult to maintain or extend.
Error 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!
}
}
What’s wrong? ❌
substitute Square
for Rectangle
Violation of expectations. Rectangles can have different widths and heights, but squares enforce equal side lengths. this GetArea
In this case the result is incorrect.
Follow LSP: Better Design ✔
To comply with the LSP, avoid forcing subclasses to behave incompatibly. In this case, detach Rectangle
and Square
Going into a different hierarchy solved the problem:
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()}");
}
}
Why does this work?
- Both
Rectangle
andSquare
Derived from Shape, but they operate independently and follow their specific behavior. - LSP is preserved because substitution respects the expected behavior of each category.
Benefits of following LSP
- Improve reusability: Subcategories work seamlessly with existing code.
- Easy to test: Code that adheres to LSP is predictable and easier to test.
- Enhance maintenance: Clear boundaries between categories make debugging and extending functionality simple.
in conclusion
The principle of Liskov substitution is essential for creating robust and flexible object-oriented designs. By ensuring that subcategories can be used interchangeably with their base categories without causing unexpected behavior, you can build systems that are easier to maintain and extend. When designing a category hierarchy, always ask: “Can this subcategory replace its base category without changing the behavior of the program?”
Following the LSP not only strengthens your compliance with SOLID principles but also lays the foundation for scalable and maintainable software solutions. Happy coding!
Let’s connect on LinkedIn and view my GitHub repository: