An introduction to Liskov substitution principle (LSP)

This post is about the Liskov substitution principle (LSP). This is also the third of my posts about SOLID principles, following the posts I wrote about DI and ISP in the past few weeks.

What is Liskov substitution principle (LSP)?

This principle is based on Barbara Liskov’s definition of subtyping, commonly known as the Liskov substitution principle which states the following:

if S is a subtype of T, then objects of type T in a program may be replaced with objects of type S without altering any of the desirable properties of that program

or in my own words, in programming we cannot always represent our objects with real-life objects and so we need to make sure subtypes respect their parents. Using the illustration below, in order to follow this principle we need to make sure that the subtypes (duck, cuckoo, ostrich) respect the parent class (bird). This means that in our code, we should be able to replace bird with duck, cuckoo or ostrich.

Subtyping

The above illustration shows clearly how in object-oriented programming (OOP) we can reuse some of our classes by making use of inheritance. This also shows very simply how using a base/parent class can be very beneficial and it is a main part of OOP.

So what is the problem you might ask? what is the purpose of the Liskov substitution principle? the purpose of it is to help you avoid some problems when modeling your objects by making you aware of potential problems that aren’t so obvious when using inheritance at the time of development.

The idea is to keep the LSP in mind when developing our classes so a parent class like “bird” can point to any of its child class objects i.e. “duck”, “cuckoo” or “ostrich” during runtime without any issues.

A classic example of LSP violation

One of the most classic examples of LSP violation is the use of a Rectangle class as the parent of a Square class. At first it seems this is the right thing to do as in Mathematics a square is a rectangle. However, in code this is not always true and this is what I meant when I wrote above that you cannot model your objects in code as you would do in the real world.

Below is an example of how in code, something like deriving a class of type Square from a Rectangle class might seem ideal…

public class Rectangle
 {
    public double Height { get; set; }
    public double Width { get; set; }
 }

 public class Square : Rectangle
 {
    public Square CreateSquare(double w)
    {
       var newSquare = new Square
       {
          Height = w, Width = w
       };
       return newSquare;
    }
 }

The example above looks fine, you can call the method CreateSquare by passing a value which is then assigned to both the height and width values of the object, this results in a proper formed square where all sides are equal. The problem arises when you define a rectangle where the height and width have different values, if you then try to substitute that object using a Square object… you will get unexpected results as the Square object expects its height and width properties to have the same value all the time.

However, the Liskov substitution principle says that you should be able to substitute any of the child classes for its base class and the example of the rectangle/square clearly breaks that rule.

The Liskov Susbtitution Principle (LSP) is one of the SOLID principles which can help you during software development to avoid common mistakes that are hard to notice if you are not thinking about them when using inheritance and modeling your classes/objects.

Happy coding!

 

15 Thoughts

  1. Square : Rectangle relationship seems unnatural to me – Square and Rectangle are types of Shapes and therefore should derive a Shape, not each other since they are separate concrete implementations of something more abstract / generic (Shape). I can understand your point here, but perhaps the example is what makes it hard for me to understand how can this principle help me find problems in my inheritance pattern in practice. Thank you for taking time to share nevertheless

    Like

    1. I think this is what he meant; he is showing that it’s wrong for square to inherit rectangle, and I agree with you that a good way to do it is to make them siblings.

      Like

      1. Exactly, my intent was to show a simple example where the Liskov principle is violated. The right approach for such objects would be to have the object Shape as the parent and both Square and Rectangle as child objects.

        Like

  2. Your Square:Rectangle example does not violate LSP as long as Rectangle does not assume somewhere that length and width are different (something it shouldn’t do anyway since some rectangles are squares). We *should* be able to model code in such a way that it matches the “real world”. If it appears that we can’t, then something is wrong with our code model.

    Like

  3. The problem is that a square object does not behave like a rectangle object, in this case because they are mutable. With a rectangle, you can set the width to 1, the height to 2. You can’t do this to a square object, because then it is no longer square.

    Like

  4. I believe the example is not showing any violation actually.

    I would expect the Square class to somehow defend its “squareness” – e.g. overloaded width and height setters so they would always set both width and height to the given value. Then, imagine a test setting height to 10 and then width to 5. Expected area of the rectangle would be 50, right?

    Once the rectangle class object is replaced with square class object I described above, the test would fail (asserting area is 50 would failed as “actual” value of the area for the square would be 25).

    This example shows the violation a bit better I think (it’s not my example, LSP was just explained to me on it)

    Like

  5. Of the SOLID principles, this one seems like it could have been left out of the mix. I’ve seen a lot of OOD mistakes, but I can’t think of any instance of this principle being violated. The fact that the square rectangle example is nearly always used to illustrate this example also suggests that most people can’t think of a real example. Let’s just agree to call them the SOID principles. Which reminds me: The principle of encapsulation often seems like an OOD problem, but the SOLID principles don’t seem to really address it. Thoughts?

    Like

    1. I don’t think so. Asking google gave out other examples (e.g. http://programmers.stackexchange.com/questions/170138/is-this-a-violation-of-the-liskov-substitution-principle or http://lassala.net/2010/11/04/a-good-example-of-liskov-substitution-principle/).

      I like the example with square myself as it takes a while before one actually gets it – you are taught that square is but a special kind of rectangle since the first day of geometry in elementary school, so usually people assume that same applies in the terms of inheritance, yet inheritance works another way – this is what makes the example so good.

      You asked for a real example, I can give you (another) one: I guess everybody is familiar with ResourceManager. It is possible to derive custom class from the manager. Now imagine that I override but GetString(string name) method. Naturally, the method returns null if Resource String (RS) does not exist. String.Empty if it’s empty and/or any other string that the RS contains.

      I decide that for my purposes, I would like to see that a RS is missing and that I want RS name (e.g. “my.missing.resource”) appear instead of empty text (anywhere in my application). So my derived class will look like this:

      private class MyResourceManager : ResourceManager
      {
      public override string GetString(string name)
      {
      var content = base.GetString(name);
      return content == null ? name : content;
      }
      }

      I will use this class instead of original one and I will derive from it or ResourceManager even more classes as application gets more complex.
      Now, code above (hopefully) clearly violates the principle (correct approach would be to create standalone class that is provided with a ResourceManager instance in constructor or something and create an interface for the class as the application gets more complex), yet you might ask why this is a problem.
      Well, once another developer gets to the code, she/he might assume that I complied with SOLID and use the method to obtained a tooltip text. Knowing how original method works, she/he might code something like that:

      private string GetTooltip(ResourceManager resourceManager, string rootResourceName)
      {
      var content = resourceManager.GetString(String.Format(“{0}.tooltip”, rootResourceName));
      return String.IsNullOrWhiteSpace(content) ? null : content;
      }

      As one see, code above prevents empty tooltips to show, however, once the tooltip resource string is not present, meaningless tooltip will be shown instead of no tooltip – this way I either have to do extra check in code or test that I have all possible *.tooltip strings present in my resource file(s).

      The example would be much nicer if ResourceManager derived from an interface (e.g. IResourceManager), however, you wanted real example 😉

      Like

      1. Thanks for the reply. The link to stackexchange was quite helpful, and your resource-manager example is also helpful. I do see these kinds of problems, but I hadn’t really connected them to the LSP.

        Like

      2. A square IS a rectangle. Code should mimic reality. If it doesn’t, there is a problem with your code, not reality. You should be able to substitute a Square for a Rectangle in every case. If you find a case where it can’t be substituted, you must be doing something with a Rectangle that isn’t valid for any generic rectangle. In that case, you need a different subclass of Rectangle (to represent the specialized rectangle) that would be a sibling of Square.

        In SOLID, how about we replace Liskov with “Loose coupling”?

        Like

  6. Well, you kinda answer yourself. Square is but a special example of rectangle – true. It’s artificial construct that is supposed to make things easier when all sides of a rectangle are same, right?

    And this simplification is the thing that is not valid for any generic rectangle – in school you probably learned (we did) that, for example, area of rectangle is a*a where a is length of the square side (all 4 of them actually). However, rectangle has 2 (eventually) different side lengths – a and b; so it’s area is a*b, rather than a*a, right?

    Once you create a class that derives from rectangle and does not differentiate the two side lengths, you are doomed 🙂 It is because relation between square and rectangle is implication rather than (at least partial) equivalence (all squares are rectangles, but not all rectangles are squares).

    Inheritance relation IS NOT implication, that is usually what causes the break of LSP. For inheritance, it is required that, in certain scope (defined by parent object), the parent and child are equal (or rather equally equipped). Simply put, square cannot be inherited from rectangle as it cannot “substitute rectangle in every case” (e.g. the case where rectangle is twice as wide as tall) and if your code does than (to use your own words) “there is a problem with your code, not reality”.

    Here is what I said in much shorter, yet popular, sentence: “If it looks like a duck, quacks like a duck, but needs batteries – you probably have the wrong abstraction.” With square and rectangle, it is same.

    Like

    1. Yes, the area of a rectangle is a*b. This is ALSO the case for squares; there doesn’t have to be a separate formula. If a=b the formula does not suddenly become invalid.

      If the original author above really means “a rectangle where the height and width have different values”, then they are not talking about a generic rectangle. The shape could be a NonSquareRectangle (a sibling subclass of Square that insists on its sides NOT being equal).

      The article at http://en.wikipedia.org/wiki/Circle-ellipse_problem does a good job of describing the problem (but with circles and ellipses) AND some possible solutions that make the common sense relationship work in code.

      Like

      1. The point about the formula pretty much aligns with the things I just said – the rectangle with all sides equally long will still fit the formula for rectangle, of course. I.e: One is still able to create object Rectangle with a = b and it will work great. It is even possible to call the object square (var square = new Rectangle { width = 5; height = 5 };) – there is no need to rename the class to NonSquareRectangle or something like that (as the class works well for square rectangles as well).

        What you cannot do though, is to derive Square class from Rectangle class as the relation between square and rectangle in real world simply cannot be translated as object inheritance in C# – that’s all.

        Since it does not make sense to change language (in the context of the blog), it would be better to concentrate on possible solutions within the language and paradigm. For example, introduce an interface (IRectangle; without presuming anything about the interface) and derive both Rectangle and Square from it (this way, one would not mess Square class with two properties that would always return the same value).

        This is what seems to me to be the point of LSP – it identifies problem pretty clearly and since one knows about the problem, it is possible for she/he to look for solutions. There are some solutions in the link you suggested (though I would personally avoid more than half of them), but the article does not say anything that would deny the problem itself or state that code “Circle : Eclipse” is equal to the real-world relation between the circle and eclipse (as in many OOPs, it is simply not).

        Like

      2. I suppose code relationships that mimic reality are the only ones my feeble mind can handle, so I’m glad that several of the solutions in the Wikipedia article make sense to me. Coding in the Liskov universe doesn’t seem very fun. I’m glad I can continue to ignore it.

        Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.