I know this is a very basic topic, but I'm curious why an abstraction in programming is always defined as a simplification/hiding of some functionality. Let's say I wrote a set of functions that let me use some functionality in some more convenient way, but I didn't exactly simplify anything, just transformed it. Wouldn't this also be called an abstraction? If no how would be this concept called?
Probably an example could help.
Let's say you want to do an HTTP request to an API and process its JSON response. Such process involves, at software level:
- Doing a DNS request in order to transform the URI to an IP address.
- Creating a socket.
- Forming an actual HTTP request with the appropriate HTTP headers.
- Dealing with TLS, which involves certificates and plenty of other things.
- Sending the request through the socket and receiving the answer.
- Parsing the answer, checking specifically whether the HTTP response is two-hundred something or not.
- If HTTP response is three-hundred, deal with redirection.
- On success, extract the JSON response.
- Parse JSON.
You can do all this yourself, if you have the skills. Or you can leverage the abstraction of different third-party libraries to do all that for you. In Python, for instance, all those steps can be done in two lines of code with the
requests library, which is an abstraction. The library itself uses other abstractions: for instance, it doesn't deal with TLS and certificates, but uses other libraries for that.
Another example: writing a file. When you write a file (would it be through your code, or with a text editor), you use abstractions: you don't necessarily want to know if the file is written to a
ramdisk, or to a hard disk, or an NFS share. Those three variants involve very different steps—writing to NFS is not at all the same as writing to local disk. Nevertheless, all three appear the same to you, because the underlying details are put behind an abstraction.
Note, by the way, that most abstractions are leaky—this is a major issue of abstractions. For instance, as a user of the
requests library, you are tempted to forget that under the abstraction, there are all those steps involved, but the abstraction will leak as soon as, for instance, your corporate DNS server is down. Or there is a problem with the certificate. Or the connection takes too long. Or you reached the number of maximum sockets. Same when you write a file: if you add a firewall rule which blocks access to NFS, the operation of writing a file will fail if the target is an NFS share. Suddenly, the abstraction leaks, and you need to know what's behind the abstraction in order to fix the issue.
To better illustrate my concern I will use the example you gave. Let's say I use a library that can "do a HTTP request to an API and process its JSON response". But very often I call this function without passing any body, just the headers, so I wrote another function, which calls this function and passes null as body. Would this also be an abstraction? I don't feel like I've simplified anything, just transformed it.
It's all about hiding something behind an abstraction. If the original function is
requests.get, and yours is
requests.get_without_body, then there is no abstraction there: you haven't hidden anything. If, on the other hand, you have your business layer, and inside you have a function
get_products, which calls
requests.get with the specific headers and without a body, then suddenly you created an abstraction: the caller doesn't know if you do an HTTP request, or query a database, or do something else.
A good abstraction allows to change the underlying mechanism without changing the interface. For instance, if you add caching,
requests.get_without_body would have to be renamed to
get_products will keep its signature: only the implementation will change. Similarly, if you need to switch from HTTP to SQL, you'll have to replace
get_products will still do the job.
Please check "The Discipline of Organizing", Section 10.3.2.3 Granularity and Abstraction.
The level of abstraction is the degree to which a resource description is abstracted from the concrete use case in order to fit a wider range of resources. For example, many countries have an address field called state, but in some countries, a similar regional division is called province. In order to accommodate both concepts, we can abstract from the original concrete concepts and establish a more abstract description of administrative region.
Also check Chapter 4 or A Philosophy of Software Design by John Ousterhout.
The term abstraction is closely related to the idea of modular design. ... In modular programming, each module provides an abstraction in form of its interface. ... The best modules are those that provide powerful functionality yet have simple interfaces.
The interface is a mechanism to achieve abstraction. We encapsulate module capability and only expose an abstract interface.
Below are some abstraction mechanisms:
- Declarative: what, not how.
- Aggregation: container, not contents
- Generalization: class, not Individuals
- Parameterization: binding details later
- Nondeterminism: leaving Choices unspecified