Abstraction Inversion

An AbstractionInversion is the design AntiPattern 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. -- GuillermoSchwarz

I disagree it is always an AntiPattern. 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 DontRepeatYourself.

> 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

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 AntiPattern 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 AbstractionInversion. We need to know how to program in ADA just to assimilate a much simpler concept... -- SeitiYamashiro

Indeed, this page (like most I've seen at this Wiki) provides AntiInformation? and AntiCompetence?. 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 RendezVous. It is explained there that it would go against the core ideas of Ada to use mutexes instead of RendezVous.


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 AbstractionInversion.

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

Now consider a simpler abstraction, CodeLocking. 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 CodeLocking. 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 AntiPattern 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). -- JeffBerkowitz

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 LarryWall's comment about trying to club someone to death with a loaded Uzi - which he offered as an analogy to the AbstractionInversion 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? -- EricHodges

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 GetTheRightAbstraction.

And all possible explanations are lies according to AllAbstractionsLie.

-- GuillermoSchwarz


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 AbstractionInversion? 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 AbstractionInversion. -- AdamSpitz

{Perhaps a case of WrappingWhatYouDontLike.}

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 LambdaCalculus, or have some notion of function located inside). Smalltalk methods, for instance, aren't objects (nor are they FirstClass 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 AbstractionInversions 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 PrimaryNoun, 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 HpCalculators?) 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:

-- ThomasTuttle?


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

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


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 http://www-2.cs.cmu.edu/afs/cs/project/ai-repository/ai/lang/lisp/impl/awk/

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. (AbstractionInversion)


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 UsefulLies, 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 AbstractionInversion. 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?


PageAnchor: "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 (LabView 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 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 AbstractionInversion: the ArcheTypes object oriented content management framework for Zope (ZopeApplicationServer). ArcheTypes is (are?) a plug-in Zope product written in the PythonLanguage, 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 PythonLanguage'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 AbstractionInversion. 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 AbstractionInversion.


Is AbstractionInversion a special case of code duplication?

Is AbstractionInversion 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 AbstractionInversion 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 RichInternetApplications an AbstractionInversion? 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 MicrosoftWord or Adobe PhotoShop, or AutodeskAutocad? 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, ThinClientHasFailed. Something new is needed.


CategoryOperatingSystem CategoryAbstraction CategoryAntiPattern


EditText of this page (last edited July 21, 2014) or FindPage with title or text search