Summary
In this episode, we explore the concept of polymorphism and its application in object-oriented programming. We discuss the importance of clarity and consistency within a hierarchy and how to avoid complexity and confusion. We also examine the challenge of the week, where we ask listeners to share their experiences with polymorphism in their own work.
Detailed Notes
In this episode, we delve into the concept of polymorphism and its application in object-oriented programming. The speaker emphasizes the importance of clarity and consistency within a hierarchy, discussing how to avoid complexity and confusion. They also examine the challenge of the week, where listeners are encouraged to share their experiences with polymorphism. Throughout the episode, the speaker touches on various key points, including the importance of the root level method, the dangers of overriding methods, and the benefits of extending and appending to results. However, some of the concepts may be unclear or oversimplified, leaving room for further discussion and exploration.
Highlights
- Polymorphism allows for a lowest common denominator across classes.
- Inheritance can lead to complexity and confusion if not used carefully.
- The root level method should be used to apply to all levels in the hierarchy.
- Polymorphism can be used to extend and append to results, not modify them.
- Overriding methods can break the hierarchy and lead to obfuscation.
Key Takeaways
- Polymorphism allows for a lowest common denominator across classes.
- Inheritance can lead to complexity and confusion if not used carefully.
- The root level method should be used to apply to all levels in the hierarchy.
- Polymorphism can be used to extend and append to results, not modify them.
- Overriding methods can break the hierarchy and lead to obfuscation.
Practical Lessons
- Use polymorphism to create a lowest common denominator across classes.
- Be careful when using inheritance to avoid complexity and confusion.
- Use the root level method to apply to all levels in the hierarchy.
- Use polymorphism to extend and append to results, not modify them.
- Avoid overriding methods that can break the hierarchy and lead to obfuscation.
Strong Lines
- Polymorphism allows for a lowest common denominator across classes.
- Inheritance can lead to complexity and confusion if not used carefully.
- The root level method should be used to apply to all levels in the hierarchy.
- Polymorphism can be used to extend and append to results, not modify them.
- Overriding methods can break the hierarchy and lead to obfuscation.
Blog Post Angles
- The Benefits of Polymorphism in Object-Oriented Programming
- The Dangers of Inheritance: How to Avoid Complexity and Confusion
- The Importance of the Root Level Method in Polymorphism
- How to Use Polymorphism to Extend and Append to Results
- The Role of Overriding Methods in Polymorphism
Keywords
- polymorphism
- object-oriented programming
- inheritance
- lowest common denominator
- root level method
- overriding methods
Transcript Text
This is Building Better Developers, the Develop-a-Noor podcast. We will accomplish our goals through sharing experience, improving tech skills, increasing business knowledge, and embracing life. Let's dive into the next episode. Hello and welcome back. We are continuing our season when we're looking at practical object-oriented programming. Today, we're basically looking at polymorphism and some of the ways that we can use that to simplify things and to improve the user experience for the developer. Today we're actually going to start getting into inheritance and talking about that. In particular, we're going to look at it from the polymorphic point of view first. That's going to be this episode. The way I want to look at this is we've touched on the idea of interfaces and we've talked about having a consistent, essentially like a naming scheme and signatures across your environment. We talked about hooks to be able to tweak those. For example, a save. Let's say there's a save function or method essentially that's going to show up in a lot of places and you give it a parameter that is the save type. Maybe save to database, save to file, share it out to somebody via email, save to a satellite, whatever it happens to be. We've got a parameterized way to look at these. We talked about that in the context of the environment of a solution of a system, not necessarily within a class hierarchy. That's where I want to add some clarifying discussion, I guess, and some points to that. When we are dealing within a hierarchy, first and foremost, it becomes a little easier essentially that you've got this polymorphic behavior because these classes that are inheriting, inherit from the base class, have shared properties, they have shared methods. There's a lot of stuff that is theoretically at least, if you do it right, I guess, there should be a lot of stuff that is accessible even to the furthest down layers of your hierarchy that are available up at the top. For example, an ID. Usually that's going to be something that's going to set up at the root. There's an ID property and there's a get ID and that pretty much just exists all the way through the hierarchy. For the most part, it's a pass-through. The same, effectively the same thing you call at the bottom of the hierarchy is it's the exact same code functionally as what you get at the top of the hierarchy. Now, this is not always the case. Sometimes we have things, a good example would be, it goes back to our print or output method where we grow as we go. Initially, maybe we only have one or two properties that are on the output. But then as we go further down, we're going to extend those. We talked about consistency and extending in the world of polymorphism before. I want to reiterate that here. The nice thing that we're going to have in, I think every language, there may be some that don't, but most languages that are object-oriented that support inheritance have an automatic way to call your parent. It's usually super. Maybe super with parentheses, maybe dot super without any parentheses, a couple different ways they may do it. Essentially what it does is it says, when I come into a method, I can call this method on my parent class. So if I've got a vehicle class and it has a method that is start, let's just say start. Well at the top level, maybe that's got an engine thing that it can start does something with engines as engine equals true or something like that. Now maybe further down, you've got one that has multiples. And let's just say we have a plane. So you have a vehicle and then you have a child class that's a plane. You have start and in the one case, it's going to start the first engine. When you get down the airplane, it needs to start the first engine, but then also start, let's say the second engine. So what you would do is you would call super and then that calls the vehicle classes or gets an instance of that, but basically it would call on the vehicle class start, starts the first engine. And then back down, the follow up to that super is that we would say, I guess, start second engine or something like that. And that would get us our two engines. So what we're doing is we're relying on the code up in the parent or maybe all the way through several parents up into the hierarchy and then just tacking on our specifics. Typically the best way to do that, and I think in some cases where it's required, but it's also usually best that you call the parent first, that you call the parent, get its results back and then the children essentially append to that, add on to the results. Now you may be modifying them and things like that, but typically you're going to go through and be adding on to the results. It gives you a flow that means you and really you got to think about it when you're when you're calling multiple instances of multiple classes within a hierarchy that you still want that method to return something that you can work with. So you're going to have a lowest common denominator that you can always work with. And then you actually within a hierarchy have essentially a lowest common denominator across each, sort of have a known lowest common denominator across your classes. And this is, you know, the nice thing is with polymorphism is you can call that method any, you know, on all of the classes because it's supported by all of them through polymorphism. But in some cases you're going to return something differently. So you may have further down the tree, there may be essentially a lowest common denominator that is not the method at the root level. But as long as you're extending and appending to the results, then you're going to be able to do that. You're going to always be growing in a certain, you know, in a predetermined direction on the results. And then you know what your lowest common denominator is. If you start mixing and matching and you start modifying the results and I guess in structural ways, then there is not going to be a common denominator. And you essentially are going to be breaking your hierarchy in your inheritance. And that is a key point. And then you'll see people just do an override and they'll just completely ignore the upper level method and rewrite or write a completely new method. They have the same name, they'll do something completely different. For example, you may have a save, you know, we'll go back to that. You have a save at the root level that saves something to a database. No parameters, it just, it knows what it's going to and it saves it to the database. Well maybe at the next layer down there's one that somebody says, oh, I'm going to override this and I'm going to save it off to a file. So completely different from the root part of it. And I'm going to pull the root properties, those values and save those off to a file. That would be a very confusing way to take advantage of object-oriented programming. It's basically obfuscation at that point because you have to know at that point what level you are in the hierarchy in order to have a feel for what's actually going on. And again, it breaks a hierarchy, which by definition, I guess, if you override, that means you're ignoring the, whatever that is, you know, the property or the value up the hierarchy chain. And when you do so, the assumption in most cases is that if you do an override, you're actually calling the parent and then appending to it. And it, again, there's reasons for that assumption, you know, the lowest common denominator effect and things of that nature. So we're looking at polymorphism in class hierarchy. It's very similar to what we want to do with a solute, with an entire system or solution. However, I would say more so. For example, if you've got a print down at the root level and then later you have a print as, you know, print as XML, print as CSV or something like that, then really what you would want to do is be able to push that stuff all the way up the hierarchy unless it doesn't make sense. And this goes back to even the most basic stuff. Let's just say we have a customer class. Actually, let's just say we have a data class and then we have inheriting from that various things. So data maybe just has an ID and a modified bio and a created date. We'll add those, you know, so basic audit fields and an ID. Everything that we have, that's what we're going to start with. Then we're going to use that as our retrieve, you know, save and retrieve to a database. Well then as you inherit, you have, let's say you have a customer and you have an invoice. Those have very different properties. But if you decide now that I want to, you know, I have a print and it's just a, you know, maybe just a print as a string, nothing very complicated. But now I come down to the customer, I want to do print as XML. Well what I'm going to have to do in that print as XML probably is also store the, print out the ID and the user, maybe the, you know, modified by and created date. So maybe not, in which case then I guess it would make sense because the, in that case the applyability, the application of the method, if it starts at a certain level, which means when you go up the hierarchy, that method has no, it doesn't make sense, then that's okay. Then we can, you know, we can add that on. Otherwise we need to think of actually creating it further up. Take care of the, in this case, the root properties that we're dealing with. Allow that to be done up at the root. And then as we walk our way down the hierarchy, then the additional things that we need to take care of at each level can be taken care of at each level. So we end up putting the code that we are, that we're implementing, the code we're writing is closest to the, I guess the home for the properties that we're working with. And this applies to other methods too. There may be helper methods or things like that, that we call that are applicable across the board. And again, maybe not. It could be things like, I mean, in the real world, it could be things like show driver's license doesn't exist in the child class, but it does in the, you know, or maybe in the minor class or something like that. Or maybe it's a young child versus minor versus adult. There's a certain point, you know, like maybe a child, if that's every child under let's say single digit ages under 10, then they're not going to have a driver's license. So you know, that wouldn't apply. So if you had a class that inherited off of that, which could, you know, could make a lot of sense. If it inherits off of that, then it doesn't need, it doesn't make sense to push that method up. Instead, you know, we put it at the right level of the hierarchy. So we got a little bit into design this time around, but I really wanted to go back and talk about that, that concept of clarity and consistency within a hierarchy as well, because it, like I said, it's the same, but there is a little bit more emphasis and a little bit different emphasis because there are definitely some cases, as there are with your entire solution, where, you know, these, the rules are meant to be broken. So that being said, let's look at the challenge of the week. What have you seen? This is sort of just because we've just talked about, what have you seen in the hierarchies you've worked with where there is a case that a method is not at the root and makes all kinds of sense, not being pushed up the chain, that it's, it only exists somewhere further down the hierarchy than the root. It's just really to give yourself some, you know, some ideas of where that makes sense. Hopefully you find one that that does make sense and not one where you look at and say, oh, this really would have made more sense if the method was actually all the way up at the root and then, you know, inherited, inherits it's way down. So we'll wrap this one up. Hopefully that gives you some more to chew on and mull over. And as always, go out there and have yourself a great day, a great week, and we will talk to you next time. Thank you for listening to Building Better Developers, the Developer Noor podcast. For more episodes like this one, you can find us on Apple Podcasts, Stitcher, Amazon, and other podcast venues, or visit our site at developernoor.com. Just a step forward a day is still progress. So let's keep moving forward together. One more thing before you go. Developer Noor podcast and site are a labor of love. We enjoy whatever we do trying to help developers become better. But if you've gotten some value out of this and you'd like to help us, it'd be great if you go out to developernoor.com slash donate and donate whatever feels good for you. If you get a lot of value, a lot. If you don't get a lot of value, even a little would be awesome. In any case, we will thank you and maybe I'll make you feel just a little bit warmer as well. Now you can go back and have yourself a great day.