Class relationships bring out the full power of object-oriented programming. Inheritance allows you to create classes that specialize or extend other classes. You can create entire hierarchies of classes but there are enough gotchas that some languages have restricted inheritance. This episode will not only explain what inheritance is but give you some guidance on how to use it wisely.
Let’s say you are modeling books. You might want a class hierarchy that looks like this:
Class name: book
Base class: none
Class name: physicalBook
Base class: book
Class name: audioBook
Base class: book
In this case, both the physicalBook and audioBook classes derive from the book class. You can then put all the data and methods that’s common to all books in the book class and then add to that of even modify the book class methods in the derived classes.
You can also have multiple levels of inheritance like this:
Class name: physicalObject
Base class: none
Class name: armor
Base class: physicalObject
Class name: helmet
Base class: armor
This says that a helmet is an armor and that an armor is a physicalObject.
Listen to the full episode or read the full transcript below.
When you’re designing your classes, look for common behavior and once you find it, you can place this common behavior in another class. Let’s call this a base class. You can now reuse that behavior and even the data members that go along with it in other classes without needing to rewrite it all. What you do is declare another class with any special or new behavior as well as the data members that aren’t found in the base class. You then say that this new class derives from the base class. Your new class is a derived class. This is inheritance.
The derived class now has all the capabilities of both classes. You can refer to the derived class as either the derived class when you want the more specific and new capabilities or as the base class when you only care about the common capabilities.
You can have multiple derived class types that all derive from the same base class but add different behavior. We actually do this all the time in real life. The concept of inheritance is nothing new. It’s actually modeled after our everyday experiences.
Want proof? Just look around you. Pick something and describe it. Let’s say you find a couple books. They’re both books but probably look a bit different. Maybe one is a paperback and the other is a hardback. They have different titles and different number of pages and different contents. So far these are all just differences that could be explained with a single class that has different data values. But what about a hardback book vs. an audio book. Now we’re getting some different behavior because you actually need to interact with these books in very different ways.
If you need to include the concept of hardback and audio books in your program, then they should probably be different classes. But even with the differences, there’s still a lot of similarity. We refer to them both as books. They both have an author. You can buy or borrow each of them. You can navigate to different chapters. In your software design, you’ll likely want a book class as a base class and a physical book class and an audio book class.
This type of categorization applies to other things in our normal experience too. Think about a tree. What is it? It’s a plant. There’s all kinds of behavior that all plants share from trees to grass. It’s also a life form capable of reproducing and dying. That’s shared by other life forms including us. We just have differences in behavior in how we go about these things. It’s also a solid object and shares those properties even with a rock.
You can keep going with this but at some point, you have to realize that you’re never going to be able to or even want to model the real world in software down to a level that no longer serves your program. What level that is depends on your program. Maybe you do need to model subatomic particles. But you’re unlikely to need both subatomic particles and trees in the same program. You have to choose what and how to model in your program. Whatever that is, just look for common behaviors to help you identify base classes and derived classes.
You can also have multiple levels of inheritance. If you are designing an adventure game, then you might have a helmet class that derives from an armor class that derives from a physical object class. In a future episode, I’ll show you how you can improve this design. Because having too many class types in a huge hierarchy can lead to quite a few problems.
You really want to look for differences in behavior when deciding if you need a new class or if you need to move some existing behavior to a new base class. Making changes like this is called refactoring. Think of it like how you factor a number into other numbers that multiple to the original number.
The problem with the previous example is that a helmet may not represent enough of a difference to really need its own class in your design. There is no one right answer to these things. In many ways software design is more of an art than a science. But there are lessons you can learn that will help you create better designs.
Assuming we do need a helmet class, it now has two base classes. One base class is its immediate base class, the armor class. The other base class is the physical object class which is also the base class for the armor class. In this design, the armor class is both a derived class of the physical object class and a base class for the helmet class.
When you have classes structured like this, you can say that a helmet is an armor which is a physical object. This means that a helmet is also a physical object. That’s why this episode is sub-titled the is-a relationship.
So far we’ve been talking about inheritance where it’s obvious that one type is another type. This is public inheritance because when you design your derived type, you specify that it publicly inherits from the other base type.
But maybe you just have some common code that doesn’t really represent an is-a relationship but you also don’t want to put the code in global methods. You can still get the benefit of inheritance and code reuse without declaring that your type is derived from the other type. It’s like a secret that only your class knows about. It can inherit from the other class in order to adopt the capabilities of the other class but hide this fact from the rest of your code. You do this by declaring that your class privately inherits from the other base class.
Whenever you construct an instance of a derived class, the compiler will first calculate the memory requirements for the entire set of classes. Remember that there can be more than just two classes involved.
Then the compiler will start constructing the base classes first and follow each class as it was declared until finally, your most derived class is constructed.
And whenever you destruct a class, the compiler will first call the destructor for the most derived class and then other destructors in the opposite order that the class was constructed.
That’s all for now. In the next episode, we’ll explore how you can change the behavior of a base class by overriding methods. And in the episode after that, you’ll learn more about private inheritance and protected inheritance.