Search code examples
c#polymorphismsolid-principlesliskov-substitution-principle

How to comply with Liskov's Substitution Principle (LSP) and still benefit from polymorphism?


The LSP says "The derived types must not change the behavior of the base types", in other words "Derived types must be completely replaceable for their base types."

This means that if we define virtual methods in our base classes, we have violated this principle.

Also if we hide a method in the drive method by using new keyword then again we have violated this principle.

In other words, if we use polymorphism we have violated LSP!

In many applications I've used Virtual methods in the base classes and now I realize it violates LSP. Also if you use Template Method pattern you have violated this principle that I've used it a lot.

So, how to design your application that complies with this principle when you'd need inheritance and you'd like to benefit also from polymorphism? I'm confused!

See the example from here: http://www.oodesign.com/liskov-s-substitution-principle.html


Solution

  • LSP says that you must be able to use a derived class in the same way you use it's superclass: "objects in a program should be replaceable with instances of their subtypes without altering the correctness of that program". A classic inheritance that breaks that rule is deriving Square class from Rectangle class since the former must have Height = Width, while the latter can have Height != Width.

    public class Rectangle
    {
        public virtual Int32 Height { get; set; }
        public virtual Int32 Width { get; set; }
    }
    
    public class Square : Rectangle
    {
        public override Int32 Height
        {
            get { return base.Height; }
            set { SetDimensions(value); }
        }
    
        public override Int32 Width
        {
            get { return base.Width; }
            set { SetDimensions(value); }
        }
    
        private void SetDimensions(Int32 value)
        {
            base.Height = value;
            base.Width = value;
        }
    }
    

    In this case, the behavior of Width and Height properties changed and this is a violation of that rule. Let's take the output to see WHY the behavior changed:

    private static void Main()
    {
        Rectangle rectangle = new Square();
        rectangle.Height = 2;
        rectangle.Width = 3;
    
        Console.WriteLine("{0} x {1}", rectangle.Width, rectangle.Height);
    }
    
    // Output: 3 x 2