Abstraction Inversion

An Abstraction Inversion is the design Anti Pattern of a simple Abstraction built on a complicated mechanism, when it would be possible (and practical) to do things the other way around.

In plain English an abstraction is a concept implemented in Code. It is called abstraction because it "abstracts" the important things while leaving the unimportant things behind. An abstraction inversion is therefore when a less abstract (more complicated) thing is used as the basis of a more abstract (less complicated) thing. -- Guillermo Schwarz

I disagree it is always an Anti Pattern. Our systems grow more complex over the years and we start to accept some things as perfectly normal. Henry Ford would probably look at modern cars and gasp at how they have 3 or 4 orders of magnitude more parts than the Model-T (if you include electronics), yet they serve our needs better than Model-T's, especially in areas of comfort, fuel efficiency, pollution, and probably reliability. The drawback is that they are no longer user-serviceable. This is true of software tools also: we rely more on vendors to get things right so that we don't have to roll our own. Even lightbulbs now have complex technology behind them and may soon have microchips in them for heat regulation, optimization, degradation management or compensation, etc.

> That example does not match the anti-pattern under discussion. The anti-pattern does not say that you can't make complex things. It also doesn't say that you can't accomplish simple things with complex things. Otherwise the implicit advice would include "don't use a compiler", which is obviously not what is being suggested.

> A concrete example demonstrating the anti-pattern is to solve this problem: Implement a function to print out text to a console, and a function to print out formatted text to a console; Which would you implement in terms of the other?

> The correct way would be to implement the formatted output function by formatting the string, then calling the plain output function so you Dont Repeat Yourself.

> The way to invoke the anti-pattern is to implement the plain output function in terms of the formatted output function, with no format placeholders. After all, it could be argued that it is just a special case of the formatted output function - you're free to argue yourself into any hole you want :)


Examples

synchronization primitives in Ada Language previous to Ada 95 were inverted because they built simple abstractions like mutexes on top of complicated mechanisms like Rendez Vous. It would have been both simpler and more efficient to expose mutexes directly and offer Rendez Vous in a higher-level library.

Ada 95 also introduces the "protected" object, which is functionally a mutex-wrapped object. So this abstraction inversion only occurs if one is using an older form of Ada, or does not know how to code with Ada 95.

Must we attach the explanation of this Anti Pattern to a concept (rendezvous) unfamiliar to most people? Can someone explain this AP with common items? Like a car and an engine? Come on people. -- Luke Closs

I think this whole page is a very example of a Abstraction Inversion. We need to know how to program in ADA just to assimilate a much simpler concept... -- Seiti Yamashiro

Indeed, this page (like most I've seen at this Wiki) provides Anti Information and Anti Competence. Think of rectangles as flexible squares, or dogs as ungroomed poodles, or cars as finless Cadillacs, or birds as emus that can fly, etc. ad nauseam. It's all pretty straightforward for anyone who actually understands the concept and activity of abstraction.

Could someone explain the Rendezvous example in English?

See Rendez Vous. It is explained there that it would go against the core ideas of Ada to use mutexes instead of Rendez Vous.

Boolean datatypes are an abstraction inversion. The language abstracts away the fact that data is all 0s and 1s, and then allows you to create a 2-byte variable to represent the same concept.

Not necessarily. The internal representation does not have to be 2-bytes. Are you referring to a specific compiler? Further, maybe 2-bytes are easier to address than one bit. It may be trading speed for space.

A boolean value is not the same thing at all as a numeric 0 or 1, even though some languages may confuse the two. Likewise, a Uni Code character is not a numeric value, although the unicode-16 encoding does map the two.

Actually, Uni Code does assign every code point a numeric value (from 0 to 0x10FFFF).

A battery is simple compared to electronic equipment. Using a battery to make electronic equipment is normal while using electronic equipment to make a battery is a not unheard of abstraction inversion.

Disagree. The battery may be a simple abstraction, and there may exist several more complicated kinds of batteries, like electronically managed mobile phone batteries, or electronically controlled directional solar batteries, but nobody said anything about the batteries factory needing to be simpler than the battery itself. Unless your battery factory is a kind of battery, which is a frightening thought. -- flj

A inductor is a simple thing - a coil of wire. But sometimes people replace it with an op-amp (en.wikipedia.org ). Op-amps are complicated things, and require more power ... and yet a gyrator is much smaller and sometimes less expensive than an inductor.

It's possible to build an emulation tower - a Mac simulator (such as ____) running inside a PC simulator (such as Ms Virtual Pc) running on a Mac. (Or the same PC simulator running on the same Mac simulator running on a PC). But that would be silly - any programs one would run on top of such a tower would run much faster directly on the underlying hardware. (However, understanding how this is even possible helps understand other ideas that really are useful - Virtual Machine, Vm Ware, Protected Memory).

There's a commercial (or was it a parody of a commercial?) that showed how *everyone* could find a [big name brand here] modem useful. In the middle of Africa, a traditionally dressed native - hundreds of miles from the nearest telephone line, power outlet, or computer - raved about how the [big name brand here] modem had high quality when he used it to crack nuts.

Using a Java Vector to implement a list of fixed size instead of an array (which the Vector contains anyway).

Markup languages as a data storage format. Program data is often serialized using a markup language (for example, Java data serialized as XML). When the markup language is parsed, its structure may be represented using programming language structures (for example, as a DOM tree). The DOM tree then contains a representation of the serialized data of the language. So you have, program data representing the markup language representing the program data. -- Richard Home

GUI's - Require tons of resources for something that would be tiny in the green-screen days.

Java Script expressions such as ((x << 1) & 0xfffe) | ((x >> 15) & 0x01) to implement a bitwise rotation, which the CPU usually has a single op-code for.

I think Java's AWT and Swing APIs fit this description. AWT is the erector set approach where you have all the little nuts and bolts to build whatever you like. Swing came along later, providing a less powerful, but much more convenient API.


Discussion

If all you have is the view at a certain level of abstraction how do you know if it is simple or complex?

Is it easier to make a rendezvous out of a mutex, or a mutex out of a rendezvous?


In response to the objections I propose the following example of Abstraction Inversion.

First, consider the Transaction abstraction (Transaction Processing). Transactions have complex semantics ("ACID") and are nontrivial to implement http://www.almaden.ibm.com/u/mohan/ARIES_Impact.html www.almaden.ibm.com. The cost of a "null" transaction is rather high in typical commercial systems - perhaps thousands of instructions.

Now consider a simpler abstraction, Code Locking. The semantics are simple to describe. The implementation can be quite simple, too, when we are given an operating system with basic support for threads.

Yet real software systems, albeit not very good ones, often use Transaction to implement Code Locking. This inversion is seen when the programming language doesn't offer primitives for code locking, the underlying system primitives are clumsy or inconvenient to access, and the application makes routine use of a transaction manager (e.g. RDBMS).

This Anti Pattern is often found in two-tier client/server systems implemented in Visual Basic (VB6 or earlier) on variants of Microsoft Windows. Cooperating single-threaded application processes implemented in VB6 often implement code locking by using database transactions as test-and-set operations.

This strikes me as a classic "inversion": a heavy, complex abstraction (the transaction) is being used to implement a light, simple abstraction (the lock). -- Jeff Berkowitz

I am not understanding the complaint. ACID might be complex to implement but not to use from the application developer's viewpoint. That is like saying that a carpenter shouldn't use electric drills but hand-powered ones instead because drills are "too complex". Non-acid techniques often result in one rogue app/process locking everyone out forever until a process is manually killed. If ACID takes more CPU, that is progress. Let the machines be the slaves, not the humans. I have seen too many human-labor-intensive "rogue-lock-hunts" in my life. If ACID is too expensive, then perhaps try ACI instead. Smaller DB engines sometimes cut out the D. -- top

Perhaps you would understand it better if you actually read the comments you responded to, which are about using transactions to implement code locks, not at all a claim that one "shouldn't use" transactions (because they are "too complex" or for any other reason). It's a bit like strapping a bunch of power drills together somehow to make a hand drill. Which brings to mind Larry Wall's comment about trying to club someone to death with a loaded Uzi - which he offered as an analogy to the Abstraction Inversion of using a hash table as a linear array.


I don't get it. Abstractions are supposed to be simpler than the things they abstract. That's why we make them. They hide complexity. What am I missing? -- Eric Hodges

Almost everything. Aside from your claims being questionable, they aren't relevant to this article, which is about inverting abstractions, not about abstractions - for instance, describing dogs as ungroomed poodles.

Sometimes a good explanation for a case is not a good explanation for another case. Case in point: The earth is the center of the universe and the sun, the moon and the planets orbit around. For the sun and the moon, the explanation is flawless, but for the planets, it needs to resort to very strange behavior. Contorted behavior I would rather say.

Then Copernicus had a not so simple idea for the Sun: The earth revolves around the Sun, not the other way around. The moon still revolves around the earth. The planets also revolve around the Sun. It doesn't look so simple at the beginning, but when Newton explained that there is only one law: gravity, it became obvious that the less massive would orbit around the more massive. That's a better abstraction. See Get The Right Abstraction.

And all possible explanations are lies according to All Abstractions Lie.


Me disagreeing too. VB in itself isn't much of a language. However, if the problem at hand is simple, and you need to implement it fast, VB is the tool for it (that is, if you can sit on top of Windows). However, many simple problems which VB is good for require synchronization across several machines. VB doesn't provide such mechanisms. So why not use an albeit very big hammer to hit the small nail? Does it make sense to first manufacture a smaller, more appropriate hammer?

Second, think of components. You often program components working with some particular data in VB. You have no idea (well, maybe you have, but not a very precise one) how your component will be used, and with what other component it will have to fight for the data - worst case scenario: two components fighting over the same XML in the same app - uit's really bad because XML doesn't provide transactions. I really don't see any other solution for VB in such cases except making sure the data source provides transactions, and using them to make sure that whatever components will synchronize properly on the data.

If you don't know how to unlock the loaded Uzi, or lack the tools to do so (for instance you have boxing gloves on your hands), clubbing the guy down is a solution. I'd say a very good one, given the circumstances. Because the proper way to do it with the Uzi is not available to you, and you also don't have a baseball bat at hand.

-- flj


Are Smalltalk's blocks an Abstraction Inversion? Scheme people are always talking about how they can build objects out of functions; Smalltalk builds functions out of objects, which I think is a lot more useful, because "everything is an object" is a much more useful kind of uniformity than "everything is a function." But maybe that's not what's meant by Abstraction Inversion. -- Adam Spitz

{Perhaps a case of Wrapping What You Dont Like.}

It is an abstraction inversion; one can have functions without objects but not the other way around. (Virtually all of the object calculi I've seen are either based on the Lambda Calculus, or have some notion of function located inside). Smalltalk methods, for instance, aren't objects (nor are they First Class entities), though it's quite easy in Smalltalk to get a full-fledged object which looks and acts like a function. This probably doesn't matter; using objects to simulate functions isn't a particularly obnoxious practice, and many OO languages do exactly that. Not all Abstraction Inversions are bad - it's a lot simpler to model a function as a special case of an object (with a single method) than it is to model objects as functions.

{But objects often force one to have a Primary Noun, whether you want one or not. If one wants a naked function, why can't they have one? That is the "simpler thing" isn't it?}

Sometimes the inversion is easier to grok than the non-inverted model.


I have a major example of how this is bad. I started to write a meta-kernel (like meta + kernel, not meta-kernel for Hp Calculators) for my Ti89 that would let you run multiple tasks at once. Things like global and local variables, task priority, and task interaction were all implemented as lists or matrices in the calculator filesystem.

The problem had to do with a few things:

The calculator didn't support functions that interacted with the user (beyond calling the getKey() function - specifically, you couldn't write a subroutine that could do something interactive and then return a value.) Any function had to be able to be evaluated numerically, so I basically had to set up an external return stack, and it got *really* messy...


Is this a (contrived) abstraction inversion? If so, what is wrong with it? If not, why not?

public List getList() { return new Vector(); }


A simple example:

Sometimes people implement a sequence of binary values as a String with characters standing for true and false (e.g., 'T' and 'F', or '1' and '0'). They then index into the String to find the 'bit':

char[] bitString ...; if (bitString[i] == 'T') { // do stuff

So, effectively we have a character representing a bit value. But a character is a sequence of bits anyway. So we have a bit sequence made of characters containing bits which are made up of a bit sequence.


Originally at SimplifiedLispDialect

The simplest Lisp I've ever seen is called Walk. It is Lisp implemented in 700 lines of Awk code. It doesn't use strings as you suggest, because that's really a bad idea. Lists are cheaper, easier to understand and faster. There is no advantage in using strings, except for having a programmer with a full time job.

See Walk at www-2.cs.cmu.edu

They should have written it in Lisp, then it would be even shorter. :-)

Now *I* know that's a joke, but someone just said the same thing to me in all seriousness. You need something from which to boot-strap, and using a language you know is a good way to help understand what's going on.

I suppose one could write a simpler dialect in a more complex dialect. (Abstraction Inversion)

You can implement a language in itself and that's usually easy, unless the language is really complex like C or C++ and you want to implement that in a poor language like C or C++. Implementing Lisp in Lisp is easy and implementing Smaltalk in Smalltalk (see Squeak Smalltalk) is easy too. Implementing Prolog in Smalltalk or Forth in Sather is not so easy though.

I'm not sure those are the best examples. Forth is famous for having tiny implementations even in assembly. Prolog has been implemented in less than 100 lines of Lisp. Implementing Lisp in C may be the kind of example you're thinking of.


This is becoming the in-style insult for ideas one is against. The thing is that everything is complicated these days, so everything is an inversion just about. Abstractions can often at best be only Useful Lies, especially for non-trivial things, and the fact that they are inherently lies leaks out on occasion. An example is languages that try to be "platform neutral". The specifics of the OS end up leaking out one way or another.

For example, to be truly neutral a language would have to either always ignore file name case or always respect it. If the language goes with the underlying OS case conventions, then cross-platform-ness is broken because files will be recognized differently and something that works on one OS may not work an another. Thus, "neutral" can be defined multiple ways, each with their drawbacks. If the language does not respect the OS conventions, then it is essentially being its own OS (or file system). In that case it is being "cross-platform" by being a platform itself, which sort of is cheating.

Good point. I don't think it is wrong to point out genuine abstraction inversions, as sometimes they do indicate broken design, but then again, there may be a good reason why it has been done that way.


I think everyone is missing the point of Abstraction Inversion. The point is the Abstraction fails to hide any of the problems associated with the original implementation, while adding its own set of implementation complications as well.

I think we need better examples. Non-trivial abstractions often have to be non-perfect, as described above, but does this alone make them "inverted"? What is the difference between an imperfect abstraction and an inverted one?


Page Anchor: "virtualTool"

Abstraction inversions are not necessarily a bad thing. For example, I once worked at a company that had a division that created "custom tools" for manufacturing systems. They were rapidly moving to software systems using mostly off-the-shelf parts (Lab View was the most common software tool). A physically-created custom part may be technically simpler, but it is often easier to "manufacture" a virtual tool now. Obviously a tool that requires a PC is far more complicated in terms of number of "parts" than a dedicated physical tool, but that alone does not make it less economical. This is because the PC is an off-the-shelf tool with a lot of flexibility. Virtual wires and circuits are often easier to connect and test than real ones.

Another example is cell-phone usage. People sometimes use cellphones even when they are in the same building. Clearly that is more complicated than a land-line, which is 1800's technology, but the flexibility outweighs the simplicity. [Written in early 2000's.]

-- top

I think cellphone usage is actually a counter-example of abstraction inversion, if you use the same criteria used at the top of this page:

It's easier and more efficient to implement Rendez Vous in terms of mutexes than the other way around. Implementing the functionality of mutexes on top of Rendez Vous is abstraction inversion: it's possible, but needlessly complicated/expensive.

It's easier and more efficient to implement transactions in terms of locks than the other way around. Implementing the functionality of locks on top transactions is abstraction inversion: it's possible, but needlessly complicated/expensive.

It's easier and more efficient to implement landlines in terms of cellphones than the other way around. Implementing the functionality of the cellphone network on top of landlines is abstraction inversion: it's possible (imagine having countless small, SIM-card reading phone terminals everywhere, connected through a landline mesh), but needlessly complicated/expensive.

It's impossible to implement landlines using cellphone technology if your landlines are using state of the art technology. Which wouldn't be copper but fiber.

The only reason you can use cellphones to implement landlines is because North American phone companies still haven't started laying down Fiber To The Home. Even long after it was initially possible way back in the '80s. Even after Japan began doing this in the '90s.

In this case, the abstraction inversion of using a cellphone to implement a landline is a bad thing, possible only because we're still using obsolete technology.


Here's a real-world example of Abstraction Inversion: the Arche Types object oriented content management framework for Zope (Zope Application Server). Arche Types is (are?) a plug-in Zope product written in the Python Language, that "provides a simple, extensible framework that can ease both the development and maintenance costs of CMF content types while reducing the learning curve for the simpler cases". It's built on top of the Python Language's object system, as well as about seven plus or minus two other pesky object systems and runtime frameworks in Zope that it tries to hide, but which still keep poking through. It's better than the alternative of dealing directly with all those weird half-baked abstractions at once, but it's much simpler and more powerful to program directly to Python's elegant object system, than to implement a bunch of scattered abstractions on top of it, and then implement another one to hide them all.


I've got a great example of Abstraction Inversion. Imagine an environment property-management system that stores application properties in xml, then when applications are deployed it loads them into a database and sets up a messaging subsystem to alert any environments that may be using those settings when there are changes through an administration interface. Then imagine that the system has no way of loading properties in batches, and instead requires manual setting of each property through the administrative interface. Then imagine that the operation staff are so scared of the brittleness of the system that they never change the properties once they are configured. Then imagine that the original developer of the system is no longer interested in maintaining the system because it's too tedious. Then imagine how much simpler it would all be to store the environment properties in property files.

Why would property files be any better than XML files? What difference does the file format make? And why is this an example of abstraction inversion?

Please re-read the description-- your comprehension has been somehow curtailed. This is not an XML vs. Property sheet rant, it is a unused, unmaintainable and unnecessary complexity vs. simplicity rant. The system described had to be lived to be believed (trust me). The ultimate solution was to replace all the baroque machinery with regular property sheets and it worked fine. This is in my opinion a definitive example of Abstraction Inversion.


Is Abstraction Inversion a special case of code duplication?

Is Abstraction Inversion a special case of code duplication, where a dependend class not only duplicates effort, but also escapes its level in a stack of abstractness layers inside an application? It seems to be common to all examples I have read so far that Abstraction Inversion occurs in conjunction with code/concept duplication in two dependent modules with different levels of abstraction, and a directed set of dependencies from the more concrete to the more abstract modules/classes.


Is the new trend to build Rich Internet Applications an Abstraction Inversion? we use heavy client application (the Browser) to run "lightweight" RIAs, that to whatever they can to act as heavy clients... in other words, it seems like everyone wants to the things normally done in Java, C++, C# or Delphi to be executed in HTML/JScript (even if they have to write those things in Java, C++, C# or Delphi)... If we want heavy client functionality... why not use heavy client instead of trying to make a web application act as a heavy client? (when we are able to implement something exactly as complex as Microsoft Word or Adobe Photo Shop, or Autodesk Autocad in a web application... our web application will be so complex an heavy that it will be impossible to distinguish it form a heavy client... or not?)

In short, Thin Client Has Failed. Something new is needed.


See original on c2.com