Most complex tasks are solved using abstractions. To create an abstraction one groups lower-level concepts, what I will call primitives in this text, and make them interact in a pre-defined way.
Abstractions are present at all levels in a system. Computers work based on electric signals. To reduce the Essential Complexity we abstract those signals with bits and bytes. Working with bits and bytes is still too noisy therefore we abstract those with mnemonics. Mnemonics are still hard to deal with so we created higher-level programming languages abstracting those.
The book Beautiful Code has a nice article by Diomidis Spinellis (also available here) that explores how abstractions work at this level.
Even though abstractions are easier to see at lower levels they are also part of the toolset the average programmer uses. If you think of it, a language like Java or C# use classes to abstract behaviour (expressed as methods/procedures) + state (variables).
But why do we create so many layers of abstraction? Edsger Dijkstra, in one of the most important essays in this industry, says:
it might be worth-while to point out that the purpose of abstracting is not to be vague, but to create a new semantic level in which one can be absolutely precise.
An abstraction should not be just a group of primitives, the abstractions you use should create a new semantic level on top of the primitives. This semantic level encompasses new terms and concepts that probably cannot be directly mapped to the primitives; the way we aggregate primitives to form an abstraction must form something that doesn’t exist at the primitive level.
Think of an abstraction as some new concept that you cannot express directly using the words you had (i.e. the primitives), something that needs completely new words to be described. This new level created by the abstraction must make it easier for you to describe your system as it gives you new words that let you express exactly what you want, with minimal noise.
Smells Like a Broken Abstraction
An example of the kind of vague abstraction that I believe wouldn’t make Dijkstra very happy is the use of Regions in C#. Regions can be very useful –if you are building embedded DSLs, for example– but the way they are sold by Microsoft is not great. From MSDN:
#region lets you specify a block of code that you can expand or collapse when using the outlining feature of the Visual Studio Code Editor.
One can say that #regions are just flexible curly braces. I don’t agree with that, curly braces in languages like C# or Java not only have semantic for the language compiler but also define a unit of abstraction, being a method, a conditional or a class.
Here’s an example of how people often use #regions in C# (extracted from Codeplex and modified for brevity):
public class GlobalRadioButton : System.Web.UI.WebControls.WebControl, IPostBackDataHandler
{
#region events
private static readonly Object EventCheckedChanged = new Object();
protected virtual void OnCheckedChanged(EventArgs e)
{
EventHandler ecc = (EventHandler) this.Events[GlobalRadioButton.EventCheckedChanged];
if (ecc != null) ecc.Invoke(this, e);
}
public event EventHandler CheckedChanged
{
add {this.Events.AddHandler(GlobalRadioButton.EventCheckedChanged, value); }
remove {this.Events.AddHandler(GlobalRadioButton.EventCheckedChanged, value);}
}
#endregion
#region Properties
public virtual Boolean AutoPostBack
{
get { return savedState != null ) ? (Boolean)savedState : false;}
set {this.ViewState["AutoPostBack"] = value;}
}
public virtual Boolean Checked
{
get { return savedState != null ) ? (Boolean)savedState : false;}
set {this.ViewState["Checked"] = value;}
}
#endregion
#region Implementation of IPostBackDataHandler
void IPostBackDataHandler.RaisePostDataChangedEvent()
{
this.OnCheckedChanged(EventArgs.Empty);
}
Boolean IPostBackDataHandler.LoadPostData(String postDataKey, System.Collections.Specialized.NameValueCollection postCollection)
{
String postedValue = postCollection[this.UniqueGroupName];
Boolean meCheckedPosted = (postedValue != null && postedValue == this.ValueAttribute);
Boolean checkChanged = false;
//...
return checkChanged;
}
private Object GetHiddenProperty(Object target, Type targetType, String propertyName )
{
PropertyInfo property = targetType.GetProperty(propertyName, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public );
return ( property != null ) ?return property.GetValue(target, null) : null;
}
}
#endregion
}
You will probably agree with me that the abstraction created by the class, methods and properties are valuable. They create cohesive and meaningful units in our domain.
What about the #regions? Is a label “Implementation of IPostBackDataHandler” helping the programmer to be precise or just telling us that maybe that logic should be extracted to somewhere else? If you need to isolate that from the other bits of your class maybe it doesn’t belong in that class at all! #regions are bad smell, they are hints that your abstraction is broken.
Sidenote: Surely people use #regions to flag blocks like the “Properties” and “Events” in the previous example. Probably this people don’t think of the #regions as abstractions at all, it’s just something to help while editing that file. We still have a bad smell though. Why do you have so much stuff –so many properties, events, methods, etc.—in a single class that you need to expand/collapse them? Maybe you have too much stuff going on in a single class?
Conclusion
As software developers, most of what we do is to create and reuse abstractions. They play an important role from the bottom of bits and bytes to the higher-order-everything nature of modern programming languages.
It is so natural and easy for a developer to create abstractions that we need to make sure they are actually adding value to our system. Death by abstraction is so common there is a very famous quote, attributed to Butler Lampson, that says:
All problems in computer science can be solved by another level of indirection
To what Kevlin Henney replied with:
except for the problem of too many layers of indirection






















