Once again, I stumbled upon the word “abstraction”, as in a recent question on programmers.stackexchange.com. Often times I see this word misunderstood and some of the answers provided embody the most common misunderstanding.
In programming, abstraction is often understood as generalization, i.e. generalization of a specific component.
A component has a specific design based on a number of assumptions about the problem it should solve. The straight-forward and popular way of generalization means, we reduce those assumptions, leading to a more general problem, to which we can provide the design (and implementation) of a (possibly) partial solution. Java has the keyword
abstract just for this, although what it actually denotes is classes and methods, that do not work due to a lack of concrete context.
For example, let’s take a component, that is called
HTTPService. We “generalize” this by removing the assumption that we use HTTP for communication. We design a new class called
ServiceBase, which provides about all functionality of
HTTPService, but doesn’t make the assumption of HTTP being used. As such, this class is useless, but by subclassing it while adding the assumption that HTTP can be used we obtain our
ServiceBase is a partial solution for a more general problem. This is not too bad. But it’s not abstraction. It is what I would call explicit generalization.
Abstraction in Human Perception
I know, this is gonna knock you off of your feet: Abstraction comes from Latin😀.
To abstract (abs – from, trahere – pull, draw) an object, means to draw something from that object. That something is its essence. The abstraction of an object is, what is essential about it. Abstract art for example undertakes the effort of trying to visualize the essence of an object, bypassing its visual appearence. Sometimes this just really WTFs you, but maybe this will enlighten you:
This starts with a depiction of what a bull looks like and ends as the essence of a bull, as Picasso sees it: Big, 4 legs, 2 horns, a tail and genitalia. And you surely agree, one can easily recognize what it is. This is abstraction.
What these few lines represent can engage in a bull fight or reproduce. Our
ServiceBase-component, which many would call
AbstractService (in fact I know at least one framework with a class named exactly like that), cannot do anything. That is why this is not abstraction.
In our everyday life, we deal with abstractions. We usually deal with the essence of things, not with the complications of their concrete nature. When we say mouse, the essence is basically that pointer moving on the screen “clicking” things. We do not even really think much of the physical device or the mechanical, optical and electronical effects at work. We could possibly decompose the mouse’s essence into moving and clicking. But nobody would ever build a mouse that only moves or clicks, or that is only an empty piece of plastic with a USB-cable coming out at the front, saying that you have built an abstract mouse, that provides a partial solution to the generalization of the problem mice can solve.
Abstraction is about perspective. About the way we see things. Consider a mouse in a computer game.
- We see the mouse as a source of movement information, which is used to control the camera.
- We see the mouse as a source of discrete signals (buttons), which is used to fire a weapon or more generally for interaction with the environment.
However, on a motion sensible device, I can use motion for camera control or I can use speech input for interaction on others. I can replace the mouse as a whole by devices, that sum up to its abstraction. I can just as well replace one role of the mouse, e.g. moving, by a different device, but this doesn’t mean I rip out the mouse ball or tape over the laser.
Abstraction in Programming
Abstraction in programming is about understanding the essence of an object within a given context. When we abstract the
HTTPService, we have an object that performs whatever “service” actually means here (probably some (possibly stateful) application layer protocol) using HTTP to do the job. If our architecture is good, then about every part of the application concerned with the
HTTPService is only concerned with the service-part of it. So a
Service here is not a particular object or class, but the concept of a service, which is best reflected in an interface (as it is called in most languages). Abstraction means, that all parts of our application, that rely on this and only this functionality of the
HTTPService, rely on exactly on this essential aspect, naturally an interface named
IService. The process of abstraction means ensuring that components do not depend on other concrete components but on mere abstractions of those.
It is important to understand, that by abstraction we do achieve generalization, only in a different way then through “explicit generalization”. Here, all components depending on the abstraction are implicetely generalized. This has nothing to do with how the concrete implementation of the abstracted component is implemented. It could be the worst spaghetti code ever on earth. It doesn’t matter, because you do not perceive it. In fact
HTTPService shouldn’t even exist. There should just be a
Service, that executes its communication through an implementor of
HTTPConnection being one of them. But this design flaw doesn’t affect the rest of the application, because it is abstracted away.
Abstraction is not only mistaken with generalization, but also with encapsulation, but these are the two orthogonal parts of information hiding: The service module decides what it is willing to show and the client module decides what it is willing to see. Encapsulation is the first part and abstraction the latter. Only both together constitute full information hiding.
The dependency inversion principle is all about the importance of the less popular part of information hiding. Assuming you had the right low level components and the right high level frameworks, programming is “only” about writing adapters to stick the first into the second. Usually, the best you get is a neat component library, tightened up in a nice facade, but sooner or later you discover you need to both do modifications beyond the facade and write an adapter layer anyway.
If there is something I would like you to take with you, it is, that abstraction is a good thing, and that the right place for an abstraction is in a given context. Trying to anticipate such contexts, i.e. designing a component for specific uses is a very good thing too. But try to keep it sensible. KISS, because otherwise you’ll probably overengineer things.