For those of you who have been waiting for the next letter of the SOLID series, we are happy to finally discuss the 'L' in SOLID, which is the Liskov substitution principle, or as we like to call it, the LSP. If you’re like us and the LSP conjures up an image of a long-gone mathematician, think again. It is actually a principle developed by Barbara Liskov, an American computer scientist. In this episode, we get into a simple definition of the LSP, which is that objects should be replaceable with their sub-types, and this should not alter the functioning of the program. To clarify, we use an example of shapes in a program and how the principle would be applied. We also look at how the LSP relates to inheritance, and why inheritance should only be used in certain circumstances. Along with this, we touch on our feelings about interfaces and learn more about William’s experience with TypeScript. Tune in today!
Key Points From This Episode:
Transcript for Episode 166. SOLID - Liscov Substitution Principal
[INTRODUCTION]
[0:00:01.9] MN: Hello and welcome to The Rabbit Hole, the definitive developer’s podcast. Live from the boogie down Bronx. I’m your host, Michael Nunez. Our co-host today.
[0:00:09.2] DA: Dave Anderson.
[0:00:09.7] MN: And our producer.
[0:00:11.2] WJ: William Jeffries.
[0:00:12.1] MN: Today, we’re going to talk about the Liskov Substitution Principle.
[0:00:16.2] DA: Yup, there is no Liskov substitute for you, William.
[0:00:23.6] MN: You need to, yeah. We missed you man, how’s it going?
[0:00:25.2] WJ: Thanks, guys. You’re so solid.
[0:00:29.7] MN: We’re just trying to be like you bro. If you noticed, we’ve been starting on the SOLID or the ‘SO’ part of SOLID, we have a couple of interviews in here and there that we wanted to release so we’re going to try to capture down the ‘LID’ part in SOLID but expect more interviews. We’ll hopefully be able to spell out an entire word within the next couple of episodes.
[0:00:51.2] DA: It’s like Game of Thrones cross Wheel of Fortune, Dragon Ball Z cross with Wheel of Fortune. What’s the next letter?
[0:01:03.2] MN: People are in suspense for the next letter, we apologize, they’ll come soon, trust us. But today, we’re talking about the ‘L’ which is the Liskov substitution Principle.
[0:01:13.4] DA: Yeah, my god, Liskov, just sounds like a mad scientist, right?
[0:01:18.0] WJ: Who did you think Liskov was when you heard Liskov principle?
[0:01:23.1] DA: Some Russian from the 18th century in a dusty library, I don’t know.
[0:01:29.8] WJ: Yeah, I definitely – old dead mathematician.
[0:01:36.0] MN: I had a math guru in my head and given the definition that you would find in Wikipedia would definitely allude to the crazy dead mathematician. I’m going to try to read this at one breath because it’s a mouthful. Here it goes. “If S is a subtype of T, then object of type T may be replaced with objects of type S, i.e, an object of type T may be substitute of with any object of a subtype of S without altering any of the desirable properties of the program, creativeness task perform, et cetera.”
[0:02:13.3] DA: What was that? It’s science, that’s what that is Mike.
[0:02:16.8] MN: Yes. Definitely a science and I know we were talking about mad scientist, dead mathematicians but this principle actually comes from a Barbara Liskov who still alive, Liskov lives and she is an American computer scientist.
[0:02:34.7] DA: Yup, from the 1987 conference keynote address titled Data Abstraction and Hierarchy. Just reading off Wikipedia over here like not going to lie but yeah, it was kind of interesting, this is actually like a principle, a very hard computer science principle that was first spoken about by Barbara Liskov and then joint published with Jeannette Wing.
[0:03:00.9] MN: Yes, she got her Turing Award for introducing the Liskov substitution principle and she was one of the first woman to be granted a Doctorate in Computer Science in the United States which is awesome. Thank you for your work, Ms. Liskov. We’re going to get right to the SOLID piece because I said a lot.
[0:03:17.8] DA: Yeah, I did go a little bit over my head, maybe we could do another take on like –
[0:03:23.2] MN: Do you want to try or I said a lot. I’m out of breath right now.
[0:03:26.7] DA: Yeah, I could give it a shot. There’s another definition here which is that objects in a program should be replaceable with instances of their sub types without altering the correctness of the program.
[0:03:40.5] MN: That was a lot nicer.
[0:03:41.9] DA: A little easier.
[0:03:43.4] MN: Goes a lot nicer, yes. The first one is definitely a whole lot of math.
[0:03:47.3] WJ: All right, let me see if I can explain this in plain English. I think the idea here is that you should be able to swap subclasses in for each other. So, for example, if you have a bunch of shapes like a triangle and a rectangle and a square and they all inherit from a shape class then you should be able to – anywhere that you could use a rectangle, you should also be able to replace that with a triangle and have everything work.
Let’s say you have an area method that’s defined on the shape class. Well, triangle needs to implement an area method and rectangle needs to implement an area method and so forth. It’s like an interface in Java or a duck type.
[0:04:32.0] DA: Yeah, I think more specifically, it’s like if I expect that you’re going to give me a shape then a circle, a triangle, whatever should do the things that a shape says it’s going to do, in consistent way.
[0:04:49.7] WJ: Right. If every shape is going to have a perimeter method and an area method, then it doesn’t really matter how those methods are calculated, it could be that the square it’s side squared and for a rectangle, it’s two L plus two W or whatever, actually that would be the perimeter but whatever. As long as the method h as the same signature then you’re fine.
You wouldn’t want to try and pass in the values that need to be present to do the calculation because that’s going to be different for every shape.
[0:05:20.3] MN: William, you're an excellent programmer and I believe what you did was the right thing in making square and rectangle and triangle inherit from the shape. But the problem is that a lot of people and I think Dave mentioned it earlier before the recording that it was a heated argument to determine whether a square is a rectangle because some people would mention that a square is a type of rectangle, but when you actually implement the programmatically, that may not be the case because a square, in order to draw a square of the area with square, you only need one side because every side is the same as opposed to a rectangle where there are two separate sides.
The question is, is a rectangle a violation of the LSP principle?
[0:06:06.9] DA: I feel like either approach is right but it depends on your context and that’s kind of why the designers of Go aware like, “You don’t get OOP, we’re taking this away from you, you don’t get inheritance, you only get interfaces because otherwise you’re just going to ruin it.”
[0:06:26.4] WJ: Yeah, I think you’re right.
[0:06:26.9] MN: Don’t ruin this for us.
[0:06:27.9] WJ: Inheritance is nice if you used with caution, you sprinkle that in lightly, you don’t want like seven layers of inheritance. I think that’s what happens if you're like, “Yeah, well, square is a rectangle so this question in hand from rectangle and rectangle inherits from shape.” Because then it’s like well, I mean, a rectangle is a polygon, should we have square inherited from rectangle and inherit from polygon, inherit from shape?
[0:06:53.3] MN: No.
[0:06:54.1] WJ: How deep do we want to go? I mean, it’s a parallelogram.
[0:06:56.7] DA: I feel like I’ve worked in this code base before and refactor is and I cry. It broke me.
[0:07:03.8] WJ: Yeah, so I am pretty hesitant to use inheritance unless you have a very straight forward inheritance pattern where you only have one layer of inheritance, you have one clear super class and then just a handful of sub-classes that are going inherit from it. So, if you know you’re really just going to have maybe like six or seven of these I think inheritance can make sense. But if you can’t come up with a good justification for why the hierarchy is going to work this particular way and not some other way then I think you are swimming in danger as worse.
[0:07:36.7] DA: Yeah, I definitely see like inheritance use in languages like Python where they allow multiple inheritance but they don’t want to have a formal concept of an interface or at least they didn’t for a long time. So, they would just use that as a way of defining what your structural sub-type was, what your interface actually was and what you needed to implement and force you to do it. But yeah, besides that just run.
[0:08:06.1] MN: Just be careful. If you are able to swap them out then do so. that whole math monologue that I mentioned before it is simply just if you are able to replace one sub-type for another without breaking your code or causing it to do some form of exception then you are probably violating the Liskov substitution principle or as I called it the LSP because I guess we had to keep it cool and hip.
[0:08:30.6] WJ: That was is what the cool kids are saying.
[0:08:33.8] MN: Yeah, you know don’t violate the LSP bro, while jump for that LSP.
[0:08:41.1] DA: It is like an afterschool special. “Oh, no Bobby. Don’t violate it.”
[0:08:46.1] MN: Don’t do it bro. I told you what will happen.
[0:08:50.0] DA: Just peer pressure.
[0:08:50.8] MN: Exactly, just think about your objects and inheritance and don’t violate it, that’s all. It is all what I am asking for.
[0:08:55.7] WJ: Does anybody miss interfaces?
[0:08:57.5] MN: Do we miss them?
[0:08:58.7] WJ: Yeah like if you are in a language that doesn’t support interfaces, do you miss them?
[0:09:03.2] MN: I haven’t thought about them in a while.
[0:09:06.6] DA: I feel like –
[0:09:07.1] MN: I am in JavaScript view land right now and doing some Rails backend, haven’t really.
[0:09:12.1] DA: I feel like kind in framework land, right? Where it is like you are in a box. I think it is good when you’re going off into like service code territory where you’re trying to extract something out and if you can at least think about what interface is and name it, then I think that is a pretty powerful thing because that way you can try and keep it consistent because with a duck typed language, it is totally possible that your duck might instead quacking return 42 or something like that and that might be kind of disastrous.
[0:09:49.9] WJ: I think I kind of miss interfaces if I am in a strongly type to language but if types are not getting enforced then it doesn’t really seem like it adds a lot of value in my life. It is not a thing that I reach for if I had.
[0:10:03.9] MN: What are you typing in these days? What language framework are you using?
[0:10:06.2] WJ: So, TypeScript on the frontend, which I like my interface and then Ruby on the backend and I don’t miss it. I don’t miss this.
[0:10:15.3] DA: You’re not using Sorbet?
[0:10:16.6] WJ: No and so –
[0:10:19.4] DA: TypeScript is pretty intense. People who write TypeScript, if S is a subtype of T, they’re like way into it. All of those parameterized types we can get pretty crazy defining those types.
[0:10:30.0] MN: I take it William you are not going crazy with the types, in TypeScript?
[0:10:33.5] WJ: No.
[0:10:36.0] MN: Any everywhere?
[0:10:39.5] WJ: I always feel guilty when I use any but odds it is so much easier.
[0:10:44.7] MN: Yes.
[0:10:46.4] DA: Just keep it simple I guess sometimes.
[0:10:49.3] WJ: Well so I am working on a codebase that it was originally JavaScript and got converted to TypeScript. I think if you start off with TypeScript, you can be really puritan about it and force that nobody uses the any type but when half your codebase is still in JavaScript that’s going to be a bad time. You’re going to have.
[0:11:06.5] MN: JavaScript is any type all around and you just deal with it. But yeah, I think TypeScript is probably a good example of when you’re able to enforce like the LSP across your codebase because you can definitely go extremely specific with the types and subtypes of what and whatnot.
[0:11:27.0] DA: Yeah but even if you are in a frameworkey land where there aren’t any types like in JavaScript like you have a React component that has some interface, sometimes it is kind of powerful to think about similar kinds of components having the same interface. If they all take the same props, then one can be swapped in the place of the other maybe and dynamically change the user experience. I have often seen that with different types of modals and things like that.
[0:11:53.6] MN: Oh man those modals and those frameworks, they’re everywhere. But yeah, be mindful when you are figuring out what types are going to exist in the codebase and ensure that your inheritance is being used properly if you are using inheritance or if you just duck typing the whole way through. Don’t violate the LSP do yourself a favor.
[0:12:12.7] DA: Be cool.
[END OF INTERVIEW]
[0:12:13.1] MN: Follow us now on Twitter @radiofreerabbit so we can keep the conversation going. Like what you hear? Give us a five star review and help developers like you find their way into The Rabbit Hole and never miss an episode, subscribe now however you listen to your favorite podcast. On behalf of our producer extraordinaire, William Jeffries and my amazing co-host, Dave Anderson and me, your host, Michael Nunez, thanks for listening to The Rabbit Hole.
[END]
Links and Resources:
Data Abstraction and Hierarchy