Design: Abstraction
Some say it’s a big word, but “big word” is an abstraction
You live with them. Abstractions are everywhere around you or, should we say, inside you. Even people who don’t like abstractions are using them, if not only through the use of programming concepts like procedures/functions/routines or objects. Because abstraction helps to manage complexity.
But of course most of the time “abstraction” refers to business or UI concepts. It can be a button, or a user.
What
The good
But what is a “good” abstraction? That is a common question asked by newcomers to object-oriented programming, for instance: they are afraid of designing a concept “incorrectly”, whereas there is no absolute bad answer. The “good” abstraction is the one that allows you to correctly represent a actor of your problem and your solution.
So there can be multiple good (and different) abstractions bearing the same name, but each differently suited to a specific need. There is no single way to represent a chair, a car, a button, or a user, since an abstraction is designed to suit to a use case with a given context.
The bad
That doesn’t mean that designing any abstraction will fit well with your need, though. You should care about designing it to meet your requirements and no more.
But even a concept meeting your requirements could be overly complex. If you implement a car as a single, monolithic concept for instance, you will have a hard time replacing a defecting carburetor, for instance. It will also be difficult to test only the carburetor without testing the whole car.
So you get it, abstraction granularity is important. You should care about not designing concepts which handle multiple concerns, which are usually bad abstractions.
When?
Actually, you’re doing it all the time. Because pattern detection is a natural brain reflex to look for similarities with what you already know. As soon as you find some, you start building a concept out of it.
But that’s what occurs in your mind. When it’s about developing software concepts, some developers will sometimes prefer to duplicate: copy and paste code, either because of laziness for refactoring, time pressure, or a because of a red alert 🚨 coming to their mind (a distortion of YAGNI called “premature abstraction”), or both.
However, usually, they should.
Why
As soon as you start to repeat yourself, you should apply the DRY principle, to benefit from:
- less code (and so less bugs) since you are factorizing/removing duplication;
- less complexity (because a matter is at a single place which hides/encapsulates its own implementation complexity and only expose a public interface);
- more flexibility by allowing different implementations of the same single concept/interface;
- a single source of truth (and so less synchronization issues, by using a single reference instead of duplicates);
- a reusable concept.
How
At the implementation stage, you need language primitives to forge abstractions. They can built from:
- functions
- data structures
- objects interfaces preferably over concrete classes, since the concept is usually about a usage contract, not a specific implementation.
- other abstractions, since the power of abstractions is to be reused. Most abstractions are composed (or aggregated) from others.
Conclusion
Abstractions allows you to:
- make sure your design is clean: define a clear and simple contract that doesn’t depend on what it shouldn’t.
- support alternative implementations without repeating yourself, you can’t forget fixing it in multiple places. This improves maintainability/robustness/readability.