Summary
In this episode, we discuss the importance of consistency and clarity in polymorphism. We explore how polymorphism can be used to give commands to objects, and how consistency is key in making sure that these commands work as expected.
Detailed Notes
Polymorphism is a fundamental concept in object-oriented programming that allows objects to behave differently depending on the context. However, it can become confusing if not designed carefully. In this episode, we discuss the importance of consistency and clarity in polymorphism. We explore how polymorphism can be used to give commands to objects, and how consistency is key in making sure that these commands work as expected. The host provides examples of how polymorphism can be used in real-world scenarios, and emphasizes the importance of consistency in polymorphism. The episode concludes with a challenge to the listeners to review their own code and ensure that they are consistent in their use of polymorphism.
Highlights
- Save and load should pair and work consistently.
- Print should be consistent in its output across all classes.
- Get ID should always return the same data type.
- Polymorphism can become confusing if not designed carefully.
- Consistency is key in polymorphism and object-oriented programming.
Key Takeaways
- Polymorphism is a powerful tool, but it requires careful design to ensure consistency and clarity.
- Consistency is key in polymorphism and object-oriented programming.
- The host's explanation of polymorphism is clear and concise.
- The importance of consistency in polymorphism is emphasized throughout the episode.
- The host provides examples of how polymorphism can be used in real-world scenarios.
Practical Lessons
- Design polymorphism carefully to ensure consistency and clarity.
- Use polymorphism to give commands to objects in a consistent way.
- Review your own code to ensure that you are consistent in your use of polymorphism.
Strong Lines
- Polymorphism is a powerful tool, but it requires careful design to ensure consistency and clarity.
- Consistency is key in polymorphism and object-oriented programming.
- The host's explanation of polymorphism is clear and concise.
Blog Post Angles
- The importance of consistency in polymorphism
- How polymorphism can be used in real-world scenarios
- The benefits of using polymorphism in object-oriented programming
- The challenges of designing polymorphism carefully
- How to review your own code to ensure consistency in polymorphism
Keywords
- Polymorphism
- Consistency
- Object-Oriented Programming
- Design
- Code Review
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. Well, hello and welcome back. We are continuing our season on practical approaches to object-oriented programming, and we are stepping our way into polymorphism. This episode, I want to talk about clarity within polymorphism. In the prior episode, we talked about what it was and how it is essentially a way to give a command to an object, or actually a series of different objects, and they all take that command and run with it. They, being the objects, have an understanding of what that command wants from them, and then they go do it. Save being an excellent example. If I tell a series of objects to save, then each of those knows what needs to be saved. It knows what matters and what commands or other things need to be done in order to save that object. Then, as the flip side of that would be load, if you load, then that means I want you to go pull yourself back from a previous state, and objects know how to do that. The key here that I want to discuss in this episode is consistency and clarity. The reason that we can do a save as a developer, that we can call a save on an object, is that we have a, within our experience, we'll say, we have a concept of what that does. Normally, it's exactly what I just laid out, is that if I have a save and a load, then save, for an instance, when later I call load for essentially that same instance, I should be in the same state I was when I saved it. That's pretty straightforward. Print would be another one. If we've got a command line, a console application, and I say print, then I'm going to expect it to put something out to the console. Now, that could be very different. It may be an ID, it may be a name, it may be some of the values, it may be all of the values, it may be formatted, it may be not, but at least there's a print concept. Print, of course, is one that pretty quickly gets us into trouble. If my objects print in a very different way from class to class, it can be very confusing. A good example would be maybe some of my objects, when I say print, they print in human-friendly text. So it'll say maybe the label for a property and then what the value is. Maybe there's other ones that when they do print, they print it in JSON format, or another does it in some sort of a CSV format, or another one does it in XML, or one does it in fixed length. There's so many ways that we can, quote, print an instance. And that's where we want to be very consistent from instance, from class to class, in how we do these actions, what the output is, and also if there's any side effects, preferably there's none, but if there's certain side effects, and particularly certain, we'll call them partner methods, that those all work consistently. Load and save being a perfect example, those pairings are going to be expected to always work. Just think of it as a developer. If I see a load for an object and I see a save for an object, then I'm going to expect that if I create an instance and I do some work on an object and I hit save, and then I come back later and I hit load for that instance, it should have all the same values. For example, like a customer instance, let's just keep it sort of blatant here. So you've got a customer, it's got a first name, last name, middle name. Well, if I do a save, and let's say I create an instance and I set a first name last name, John X Doe, first name, middle initial, last name, I save it, and I load it back and I take a look at it and it says John Doe, and I lost the middle initial, then that's not good. And technically, I would probably call that a bug. Now you may say that, oh, well, we didn't design it that way. Well, from a consistency point of view, it needs to be. It needs to, those two need to pair and work. Otherwise, we have this side effect, essentially, of, oh, you know, middle initial is not really that important, so we're not going to bother loading it back when we load an instance. That can cause problems. Now, it may be that there's nowhere that that middle initial is ever used, so it doesn't really matter, and we'll put that in quotes, but it does. We want to be consistent and we need to provide that as we are building out these methods. And it's one thing to be consistent and have the pairings work within a single class. If I load and save a customer record, then I can walk through and make sure that everything tests out right. But I need to have that same comfort, I guess, or that same experience when I go to different classes. So, for example, let's say that customer, every property that exists for a customer, when I hit save, it saves all those, and when I hit load, it loads all of them back. But let's say that we have another, let's say there's an address type that's an instance out there, and it doesn't. Maybe it has an ID and a name and a code and some other things that it does. Maybe it's got like a geocode and some stuff like that that it doesn't actually deal with. It says, no, I'm not going to bother saving those values and loading them back. No, it may be that the save and load works, I'll say, correctly for that address in the address scope, where everything that it saves is also exactly the same stuff that it loads back, and you don't have to worry about that consistency. But I do have to worry about a consistency from object to object. And this goes back to print. If I do a print, if I print every class that exists, instances of classes that exist in my application, it should be the same general thing that comes back. So I shouldn't have some that print in CSV and some are XML and some are JSON and some are some sort of human readable format. It's also a consistency. So if I have a class where I do, let's say I generally have print, which is human readable, then ideally, if I have a print in XML method that I create, I should have that across all of them. It's not 100% because there may be some objects that need to be serializable to XML and others that don't. That's OK. But if I do, I should have print in XML should be the call that I use across all of them. That's the polymorphism piece. It's a consistency piece. And this is where design really steps in, because if I call it print in one class, that same print should have the same impact or result in the other one. I should not have print in one that does human and print in another one that does XML and print another that does CSV. I should be consistent. So if I have print that is human format, let's say there's another class where I don't need human format, then rather than being a print, but I do need XML, rather than there being a print that kicks it out in XML, there should be like a different name. There should be a print in XML that prints in XML. Or maybe there's a parameter that I can send that gives me some sort of format type. But I need to think about that while I'm designing out these classes so that I have a consistency across them. So where some classes have slightly different expectations or outputs, those should not have polymorphic functions that have an implication that's different from another class. So print, this goes back to imprint, if print in XML exists in one, then that's pretty well defined. But if it's like I said, if it's print, it shouldn't be one format in one and another format in another class. We need to be consistent. We need to be consistent. We need to think about how are we implying the use of that method or that attribute from object to object. An ID is really sort of a common thing as well. If we have an ID and it is a, let's say it's a UUID, a globally or universally unique identifier in some classes, but in others, it's some sort of an auto incrementing integer. And another one, maybe it's some sort of a primary key string, it's some unique string value of that object. For example, you may have, we see this in like lookup things. So for example, you have a customer status of, let's say, new prospect and I don't know, actual customer. It was like paid or something like that or sold. Then you may have like the code in EW, SOLD and PROC are maybe like the short codes that are unique identifiers for those statuses. You want to be, if you do get ID as a method, then you want that to be the same data type that comes out for each of them. Now you can change signatures and stuff like that. So they would know, oh, in this case, it's an integer. In this other case, it's a string. But that becomes really confusing to a user if get ID is different types and different formats and all this other stuff. So I should have an ID across all of them. If my ID is a UUID, is a universally unique identifier, then get ID should always return a UUID. If it's an integer, it should always return an integer within an application at least or within a framework or within a library. So we want to have that consistency across our objects, across our classes. And we see this in things like getters and setters is that we've got a bunch of attributes. If I want to get a value, I'm going to get attribute value name. If I want to set it, I'm going to do set attribute value name. Those kinds of things are a form of polymorphism. But it's, I mean, it is, I guess, in a sense, because if I do get ID in one and I do get ID in another class, I'm going to expect the same stuff back. But this is where we have to think of it as we are building a language for interaction with our application or framework or library. And so much like in a spoken language, we have dictionaries and things like that in context that matters. We need to keep that in mind as we build out our framework and libraries as well. So be consistent as we're thinking about this polymorphism, which is very powerful, but also can become very confusing very quickly. So I want to keep that nugget. I want to focus on just clarity and consistency when we're using polymorphism as we sort of will wrap up this one and then we'll tackle another facet of this as we go into the next episode. Challenge of the Week. Are you consistent? And looking at the classes that you've created, are you consistent in how you frame actions, how you have built out polymorphism either directly within classes and subclasses, but also essentially across your class library that you're using for your application? And if not, you may need to take a look and do some redesign. But that being said, hopefully you can look forward to a great day and go out there, 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 Noir 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 developernoir.com. Just a step forward a day is still progress. So let's keep moving forward together. One more thing before you go. Developer Noir 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, be great if you go out to developernoir.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.