Thursday, September 3, 2015

Liskov Substitution Principle in .NET

In today's blog post, I will be discussing Liskov Substitution principle i.e. "L" in SOLID. I have already discussed Single Responsibility Principle here and Open-Closed Principle here.

Liskov Substitution Principle

According to LSP, the derived classes should be substitutable for their base classes without altering the correctness of the program. In other words, when implementing inheritance in your application, instead of "Is A" relationship, think in terms of "Is Substitutable For" relationship.
Let's look at the famous sqaure-rectangle example to better understand this:

Geometrically, a square is a rectangle. So this may lead us to derive square class from a rectangle class.

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

        public int GetArea()
        {
            return Height*Width;
        }
}


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

        public override int Width
        {
            get { return base.Width; }
            set { base.Height = base.Width = value; }
}


Imagine, we have a test method like this:

 public void TestRectangles()
{
            var r1 = new Rectangle()

            {

                Height = 2,

                Width = 3

            };

            var r2 = new Rectangle()

            {

                Width = 3,

                Height = 2

            };

            var isHeightEqual = r1.Height == r2.Height;

            var isWidthEqual = r1.Width == r2.Width;
}

 So isHeightEqual & isWidthEqual will be true.

However, if we substitute this test example with Square instead of rectangle, we will have:

public void TestRectangles()

{

            var r1 = new Square()

            {

                Height = 2,

                Width = 3

            };

            var r2 = new Square()

            {

                Width = 3,

                Height = 2

            };

            var isHeightEqual = r1.Height == r2.Height;

            var isWidthEqual = r1.Width == r2.Width;

}


So now isHeightEqual & isWidthEqual will be false. As a client, the correctness of my program has been compromised by substituting base class by derived class. So it is a violation of LSP.

One way that you might wanna structure this is by having a Quadrilateral base class and derive Square and Rectangle class from the Quadrilateral class.

public class Rectangle : Quadrilateral
{

        public int Height { get; set; }

        public int Width { get; set; }

        public int GetArea()

        {

            return Height*Width;

        }
}



public class Square : Quadrilateral
{

        public int Size { get; set; }

        public int Area()

        {

            return Size*Size;

        }
}


So a square and rectangle are substitutable for quadrilateral.

Another way might be to not use square class at all. Just use the rectangle class and set height and width equal manually and use it in your code.

Conclusion

This is a simple example to demonstrate the LSP. If our derived class is not perfectly substitutable for base type, then it means that we should start looking at the restructuring of our classes, if possible. Otherwise, it can lead to side effects and may lead to incorrect behavior in the system. Inheritance is a powerful tool in developer's arsenal but it should be used carefully. We should give thought to external behaviors of the entities we are using in inheritance and be able to substitute one for another.

For future updates to my weekly blog, please subscribe to my blog via the "Subscribe To Weekly Post" feature at the right and follow me on Twitter. Until then Happy Coding :)


No comments:

Post a Comment