A colleague of mine recently accused me of speaking too precisely. He was
giving me a bit of grief, but I took it as a compliment. In this
industry, we torture words until they give up all meaning. Keep your
eyes open for the next time you read the word "significant." Does it
actually refer to something that signifies? Or is it just salt,
sprinkled in for flavor?
One of the words I see overused is "abstraction." I hear developers use it when they talk about the DRY
principle and reducing common code. Reducing common code to eliminate
repetition improves your leverage, but not necessarily abstraction. Leverage allows you to make a change in one
place that affects all uses.
As an example, think about one of my favorite code-eating patterns in
OO: the Null Object pattern. It
concentrates a lot of "if not null" code into one place, which
means you can change the logic in that one place. That's leverage:
you can make a small changes with large-scale effects.
"Extract method" and "extract superclass" transformations
fall into this camp. They add leverage, which is useful.
By drawing this distinction, I'm not criticizing leverage. It's useful and important. But
there's an additional thing to consider. When you extract common code to gain leverage, you
have little ability to ignore details. Indeed, the specific case is
often tightly coupled to the implementation of the general case.
(I will admit that when I distill code down this way, it sometimes clears away the cobwebs enough to see the abstraction that was lurking in the corner. That's always a lovely feeling. So leverage can lead to abstraction.)
The essence of abstraction is cleaving. Abstraction
separates a manifold thing into independent concepts. By separating
the concepts, it also allows us to see connections between things that
share one of those concepts. So abstraction also illuminates connections
For example, consider Clojure's seq (pronounced "seek") abstraction. It calls out the concept "Sequential Iteration," thus bringing together everything that acts like an ordered sequence that can be walked. At the same time, it separates that concept from others. For a vector, seq separates that concept of "Sequential Iteration" from the
concepts of "Random Access," "Stacklike Object," and "Lazy
Evaluation." For a string, seq separates "Sequential Iteration" from "Unicode Storage" and
"Regex Matching." That's the part about separating a manifold
thing. At the same time, "seq" shows us that there is an important
characteristic shared across all those implementations.
In each case, the abstraction allows us to emphasize a particular
aspect of the thing while disregarding the rest. This is the dual nature of
abstraction: it obscures and reveals.
As it happens, good abstractions also provide leverage, because they separate concerns. I think this is why the words get conflated. All abstractions offer leverage, but not all leverage is an abstraction.
As you work on your designs, think about where you can split those conceptual atoms. What ideas can you separate? How can you then reconnect the shared concepts?