Recently on Twitter I noticed quite a lot of people spoke up about having trouble with object oriented programming (OOP). I didn’t realize this was a point of confusion for so many people. I’m not gonna try to teach anybody this subject from whole cloth. I assume you have already read some of the many, many materials out there, and probably you have even written some OOP style code already.
The promise of OOP is to improve code along two directions. OOP offers inheritance, and it offers data encapsulation. In this post, I will only be talking about inheritance. I will try to make the value of inheritance concrete and show you a real world example.
The common textbook example of a Dog object inheriting from a Mammal object is contrived. If your goal is to make a Dog object, I do not recommend building a class hierarchy of the form PhysicalObject > LifeForm > MulticellularLifeform > Animal > Mammal > Dog. If what you need is a Dog object, then just write a Dog object.
That said, there are legitimate cases where inheritance is useful, and I’m going to explain one that is closely based on an actual business need I have been working on for the last few weeks in real life.
Suppose you work for a company that does business in several states. One of the important goals at the moment is to write a transformer that changes one data type to another. This is a ubiquitous real-world problem. These data formats are pretty much the same in all states, but there are a few differences for each state that are important to the business.
You could write a script for each state that does everything, but these scripts would include a lot of duplicated code, because the script for each state would have to handle all of the common stuff that is exactly the same for all the states. Code duplication is bad. Here are just a few reasons why code duplication sucks.
- The person who comes after you can see that these large blocks of code look identical, but it’s not clear if they ultimately do exactly the same thing or if there was some good reason why you had them separated. Clearly, there are *some* tiny differences between them, but it seems clear that those differences could have been factored out. In the face of this, the next guy will probably just write another new script for a new state, and produce a lot more duplicate code (and work).
- If there is a global change that will affect this data type transformation for all states, then it will have to be done for each state script, which will tend to make the duplicated code drift apart even though the common parts should remain exactly the same. There’s no logical reason for making them different, so they should not *be* different. Even just having the same logic written in slightly different ways is bad because it begs the question: is this code actually doing something different or not?
Instead of writing independent scripts for each state, we decide to write a transform class that handles all of the common logic. Everything that *can* be handled at this level—all the stuff that is not state-specific—will be handled in this GeneralDataTransform class. To handle the small differences for each state, we write state-specific classes that inherit from the general class, so we have for example CaDataTransform for California. The CaDataTransform will handle *only* the logic that is specific to California. All of the common logic is directly inherited from the GeneralDataTransform class. Specifically, that means that the code written in GeneralDataTransform does not need to be copied. Because CaDataTransform is a child of that general class, it automatically inherits its methods.
It will require some hand-tooling to organize the logic in such a way that CaDataTransform can override the methods and data that it needs to override without overriding some top-level method that would require duplicating everything in GeneralDataTransform. Writing good code this way is not a brainless activity. Just having a class hierarchy does not mean you have intelligently used inheritance to write clean code. It does require some careful thought and clean coding skills.
The result is code that is easier to maintain and more robust against future expansion. In the future, if you need to add a new state to the business, you can probably re-use the GeneralDataTransform class exactly as it and just create a new state-level transform class for the new state. That’s time saved, so it’s money saved. That is a direct contribution to the bottom line.