Posts Tagged design
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 :D.
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.
DISCLAIMER: I’d like to point out, that the issue I am addressing here transcends the field of software developement, but this would drastically go beyond the scope of what I intend this blog to cover.
Human thinking tends to revolve a lot around boxes. Why? Boxes are useful. During everyday life, we constantly bump into objects of our perception and we tend to put these into boxes. There are two reasons:
- The most important “feature” of boxes is, that we can (mentally) carry around multiple objects in just one box. We can say “I like classical music”, instead of iterating all classical pieces and saying we like them.
- Up to a certain point, boxes are even a boost to everyday perception. If there is an item a, and a box A with many items that share common features with a and one-another, we can put a into A. We tend to infer from that, that a is likely to also share features of other items from A, without actually inspecting it. The nice word for this is “induction”, the common word is “prejudice”. It is a way of concluding, that is very efficient, because it doesn’t require a thorough study of the subject, but not without risk, because it quite often yields poor conclusions.
None the less, the main point is, boxes are useful, because we can put objects into them, which helps us dealing with the enourmous complexity of the world we live in. I suppose, we all agree, at the bottom line, the purpose of boxes is to put objects into them. I also suppose, we’d agree this doesn’t imply the purpose of objects is to be put into boxes. Yet we often behave like that. We like boxes. We can label them and then put objects into them, saving us the work of labeling all items individually.
It is my belief however, that for any kind of task, that requires problem solving, having boxes is not the biggest quality. Having boxes is good. Nowadays it is a popular oppinion that thinking outside the box is the new “shizzle” and will solve all your problems. I don’t think it will. It will perform just as poorly as thinking inside the box, because either way, the box is the limit.
The point is, to think past the box. The box is a support, a tool, and as such should not be in your way. The actual “shizzle” is the ability to build boxes and to choose clever labels. It is a more less mechanical task to sort a set of items into a given set of boxes. The stroke of genius is choosing a certain way to box things. Thus the important quality is the art of “meta-boxing”, the tool that transcends boxes, the understanding of why certain boxes are chosen the way they are and being able to replace or add boxes as the problem, one is solving, evolves.
The actual art of problem solving consists of looking at the problem in search of the right perspective, until this sweet moment comes, where all parts fall into their places and the solution becomes obvious. You’re less likely to find a good solution if you try to use the same approach you used on an old problem you consider similar. Probably it even isn’t, but your perspective is so poorly chosen, it looks similar. You’re also quite unlikely to succeed, if you try hard not to draw knowledge from problems you’ve already solved. The word is “why” and the task is to understand why you succeeded in solving other problems. Why your solution worked. To understand how you found the right perspective. And apply that understanding.