Lesson 3: Abstraction
Breaking Up Problems
Abstraction is a tool we can use to make our code easier to understand. When coding some system, the system may become too complex for a person to easily read and comprehend. Too many
s, too many loops, and too many scattered function calls may leave the reader (either someone else, or future-you) unable to grasp the code. This problem can be lessened by abstracting some functionality out.A real world example:
Your neighbor, John, walks up to you as you are about to mow your lawn. "What are you up to?" he asks.
You respond, "Well, I was just about to apply a forward force to this machine while gripping the bail bar. Squeezing the bar will allow the carburetor to-"
Your neighbor will quickly become annoyed - he doesn't want the technical details, he wants the big picture.
He wants to know what you're doing, not how you're doing it.
This is the essence of abstraction: abstracted code provides a big-picture overview,
Notice how the code on the right is much more readable than the code on the left. The code on the right may not have all of the details, but one can understand the purpose of the code at a glance.
Helper Functions
Pulling out the functionality of a lawnmower into its own class was very helpful! We were able to take a large chunk of code and condense it into a few lines. Sometimes, however, an entirely new class is overkill. Repetitive, bulky code that does not necessitate its own class can instead be pulled out into a
.Imagine you are writing the code that manages a nuclear power plant. At several points in your code, you type the same lines, going through the same meltdown prevention procedure. This procedure pulls out the fuel rods, activates the alarms, contacts the fire department, and sends a text message to the head engineer.
If you were to accidentally omit any of those instructions, the results could be disastorous. Plus, writing them out by hand each time is tedious.
Instead, let's pull the code out into a helper function.
Now, writing a new shutdown condition is as easy as writing a single function call. As an added bonus, there is no chance of forgetting to pull out the fuel rods.
Structs
We've learned how to pull data and functionality into a class. If there is no data, we've learned how to extract only functionality into a helper function. Now, let's learn how to extract data into a struct in the case that there is data, but no functionality.
Congratulations! Your nuclear power plant's safety procedures impressed the local government so much that they are funding an expansion to the plant. Previously, you had a set of variables describing the single reactor - internal temperature, external temperature, fuel remaining, time running, and turbine speed. This worked fine for one reactor, but the expansion will be adding another 3 reactors! You may be tempted to make the variables "interalTemp2", "internalTemp3," and so on, but that's awfully messy, and only becomes more messy with each expansion.
Instead, let's describe a
that holds all of the data for a single reactor. Then, we can create an instance of the struct for each reactor to store its data in.Abstraction refactoring is not limited to only creating new classes. You may also refactor your code by changing inheritance - perhaps classes A and B have an "is-a" relationship, in which Class A can become a child of Class B or vice versa. Or, conversley, perhaps a class that previously inherited from a parent class doesn't *actually* have that much in common with its parent, in which classes the classes can be
.