Table of Contents
I like Noel Rappin’s definition that “an abstraction is a structure that represents a concept and hides internal details such that the rest of the code can interact with concept via the abstraction without having to worry about the implementation details.” Abstractions are the solution to the problem that our software keeps growing but the capacity of our working memory remains more-or-less fixed. We put several lines of code within a function so that, outside of that function, we don’t need to think about those several lines of code anymore. Likewise with several functions into a module, several modules into a library, and so on.
The sole purpose of an abstraction is to avoid the need to think about every line of code when changing software. If we put several lines of code into a function but when calling the function you still need to keep in mind what those lines of code are doing, that’s a leaky abstraction and often harder to work with than no abstraction at all.
Identifying Abstraction Opportunities
From the same post mentioned above by Noel come some helpful clues as to when some code may be ripe for abstraction:
- Branching logic that uses the same attribute to branch in multiple cases. In OO languages, this is often a tell that you have subclasses or value classes or something.
- Multiple methods in a class that all get passed the same parameters. This often means you can create a class with those parameters as instance values.
- Multiple methods in a class that have the same prefix or suffix in their name. The naming may be trying to tell you that those methods are related.
- Private methods. In Ruby, I think private methods often mean you want a separate class collaborating with the main class. Other people think this is a nonsense opinion, so take that for what it’s worth.
While Noel is talking about object-oriented languages, I think these same heuristics apply to functional languages as well, if perhaps with some different terminology.