Design Pattern Evangelist Blog

Smart pointers about software design

What Is Abstraction?

Abstraction is separating the WHAT from the HOW in the design and implementation.


Introduction

I’m a volunteer mentor for computer science students at the university where I acquired my undergraduate degree. I visited campus last month for homecoming weekend. While there, I had some time to meet face-to-face with Scott, one of my mentees. Since I live about 250 miles from the university, most interaction with my mentees is electronic, so this was a rare opportunity to meet with him in person.

Scott and I grabbed a quick bite at an off-campus pizza shop directly across from one of the engineering buildings. I asked him about his summer experiences and how his final year before graduation was shaping up.

Scott knows that I’m a fan of Design Patterns, but he wasn’t sure how the patterns fit into what he’s been studying. With only my words and waving hands for presentation I compared Data Structures to Design Patterns. Data Structures solve well defined fixed problems bounded by a limited scope, whereas Design Patterns define solution approaches to recurring flexible fuzzy problems not necessarily bounded by scope. I confessed that developers may need to experience real-world problems in industry before they appreciate Design Patterns. I used a few analogies such as:

Then Scott asked, “What is Abstraction?” What are they teaching kids in college these days?

I tried to give him a quick answer, but we were running out of time. I promised him I’d follow up with a more complete answer, and that’s the main purpose of this blog entry.

What vs How

Though I chide academia for not explaining abstraction, the word abstract has appeared in 50% of my previous blog posts, and I haven’t defined it either.

I devoted the Getting the Right Abstraction is Hard blog entry to abstraction over a year ago, and I assumed my readers knew what I meant.

I suspect that definitions for abstraction vary. I included a few Abstraction quotes in Bumper Sticker Computer Science and Software Engineering.

Google’s AI generated this definition for me:

Abstraction is a fundamental concept in software engineering and computer science that involves removing unnecessary details to focus on what’s important. It’s a way to make systems more generic and easier to understand.

Other online references also focus upon removing details. I don’t view abstraction as removing details as much as I view it as moving details. Those details still exist. Abstraction is about the management of those details.

An Abstraction, such as an interface in Java, declares WHAT behaviors are provided. That is, an interface is a set of methods that declare WHAT those methods are and do without defining HOW those methods will do them. It’s a contract.

The details, which define HOW the interfaces will be implemented, have been moved to the concrete classes, which implement the interfaces.

This is evident in the Strategy Design Pattern, for which an interface may have multiple implementations:

Basic Strategy

The Strategy Design Pattern generally does not indicate how references to Shape find their way into the private List<Shape> shapes attribute within ComputerAidedDesign. I introduced a way to address this in the Dependency Injection blog.

The following diagram enhances the previous diagram with the addition of Dependency Injection and a few more diagram elements:

Strategy with Configurer and design boundaries

Abstraction makes this design more maintainable. When new Shapes are desired, such as Trapezoid and Rhombus, no updates are required above the red horizontal dashed line.

New concrete Trapezoid and Rhombus classes implement Shape. ComputerAidedDesignConfigurer will be updated to know about the existence of the new concrete classes as well.

Ease of maintenance continues in the Concrete region as well. The existing concrete classes are not affected by the addition of the new concrete classes.

Strategy with more concrete classes added

One may realize that Rectangle, Trapezoid and Rhombus are four-sided Shapes. What if more four-sided concrete classes were added, such as Parallelogram, Square and Quadrangle? We might observe common code among the concrete four-sided Shape classes and decide to refactor them to reduce duplication and consolidate related classes.

If we choose to refactor or redesign the concrete classes below the red horizontal dashed line, we can do so without concern of breaking any behavior that resides in the abstraction above the red horizontal dashed line.

Abstraction Is Not Obvious

Strategy is only one example of abstraction. Functions, procedures and methods with meaningful names that document what the code does are probably the most common forms of abstraction as championed by the Extract Method refactoring technique.

The right abstraction may not be obvious. See: Getting the Right Abstraction is Hard

The ability to abstract wasn’t available in early programming languages. The first programming language I learned was BASIC in the 1970s. Each statement had a line number, and that’s how program flow was directed via GOTO and GOSUB statements. There were no subroutine names. Subroutines didn’t have arguments or return types. All variables were global. And from what I recall, variables could be no more than two characters long.

As programming languages advanced, they supported more abstraction. Abstraction is not for the benefit of the computer. Abstraction is for the benefit of the software developer to better convey and understand the intent of the implementation.

Abstraction In the Real World

Real-world abstractions abound around us. Once you see them, they’re easier to see and understand in code too.

Cars

Driver's side car interior

Cars are complex machines, yet most adults can drive unfamiliar cars easily. Rental cars are often different from what we drive at home, but most people can hop into a car and drive it off the rental lot.

Most driving controls and the dashboard are an abstraction of the driver’s intent. The driver uses the steering wheel, gas pedal and brake pedal to tell the car what to do. The under-the-hood mechanics is how the car satisfies the driver’s intent.

The dashboard displays pertinent information to the driver, such as speed, miles, remaining fuel, warning lights, etc. This is mostly an abstraction as well.

Tachometer

There’s one dashboard instrument that I’ve never understood why it’s presented to the driver – the tachometer. I’m not a car enthusiast, so maybe there’s a reason to display the RPMs of the engine that I don’t know. My last three cars had tachometers, and they were automatic transmissions. My first car was standard transmission, and I rarely looked at the tachometer to decide when to shift gears.

The tachometer feels like a Leaky Abstraction to me. It’s telling me design details about the engine that I don’t need to know even if its benign exposure. We want to avoid leaky abstractions in our designs. We don’t want to expose implementation details to our users via the abstraction. Any concept that’s exposed to users will become an operational dependency by a user. If a project leaks an implementation detail via an abstraction, then it’s part of the interface.

Woe be the software project that leaks its database schema. It will become part of its API.

Wikipedia

Each Wikipedia page focuses upon one topic. The page’s title is its abstraction which briefly describes what the page is about. The rest of the page contains topic details.

Alice in Wonderland with the Rabbit

Most Wikipedia pages contain embedded hypertext links to other Wikipedia pages for additional information. While there’s basically one layer of abstraction for each Wikipedia page, there are many layers of abstraction within the Wikipedia environment. As readers follow the embedded links they dive deeper into details for additional context, but it’s the readers’ choice to do so. Sometimes readers go so far down the Wiki Rabbit Hole that they forget where they started.

What if a Wikipedia editor didn’t want to create a new page and reference it via an embedded link? What if they put the new content in the current page, even if that content strayed from the page’s original topic? And then another editor added more content to that content further straying from the page’s original topic.

If this pattern continues, the page will soon become bloated with off-topic details. The reader may have to read the entire page, but it may not be obvious which content is part of the original topic and which content contains supporting off-topic content.

Fortunately, I think that Wikipedia editors tend to keep most pages focused upon the topic and link to other pages as necessary. I can’t say that all software developers follow the same practice.

In the same way that each Wikipedia page is about a core topic, I feel that each method, function and procedure should have one core responsibility, which is known as the Single Responsibility Principle (SRP).

So much complexity in software comes from trying to make one thing do two things.Ryan Singer

A method’s name is its abstraction declaring what it does. The implementation contains the details of how it does it. Rather than creating a new method or class and referencing it via the method name, all too often, developers will place the new code in the original method even if it strays from the method’s single responsibility. It’s a shortcut made for convenience.

I’ve encountered methods that are hundreds of lines long, which obviously violate SRP. Each of these methods grew slowly – one shortcut convenience at a time.

Most visual editors can display at most about 50 lines of code on the screen. Any part of a method which I cannot see, I must retain in my head to understand the behavior implemented with a method. If a method is 500 lines long, I can only see at most 10% of the method at any given time. I’m too old to hold the remaining 90% in my mind.

Rollercoaster

Methods tend to get long when an implementation jams multiple layers of abstraction into one method. Reading the method takes the reader on a nauseating roller coaster ride from high level business concepts to low level infrastructural details and back again. If segregated into separate methods or classes along abstraction boundaries, then readers would only need to examine those lower-level abstractions if necessary.

Decomposed Car

Real-world concrete abstractions, such as cars, have multiple layers of abstractions as well. A car is comprised of systems, such as the engine, transmission, drive train, braking system, electrical system, etc. These systems have their own components. Eventually the entire car decomposes itself to a set of parts. Real-world abstractions deconstruct themselves to their most basic parts.

Wikipedia is different from cars. Follow the links of a Wikipedia page, and you end up at another Wikipedia page. No matter how many Wikipedia reference links you follow, you still end up at another Wikipedia page that mostly looks like the others. It’s a single topic page with words and links to other pages.

Software is the same. As the reader descends into method implementations, each method will tend to look like other methods. Code that implements high level and low level concepts will tend to look the same. It will consist of if statements, for loops, calls to other methods, etc. This similarity is what makes it difficult to separate business concepts from infrastructure details when they are intermixed in the same method.

Layers of abstractions in Wikipedia and software are fractal. Regardless of where you reside in the layers of abstraction, it all tends to look the same.

Fractal

The fractal nature of software is a double-edged sword. On the positive side, our abstractions are not constrained by the physical world. On the negative side, our abstractions are not constrained by the physical world. We can design our abstractions anyway we desire. We do not have any real-world guardrails to keep our abstractions in check.

Our abstractions, right or wrong, are our responsibility. Get the abstraction right, and code will practically write itself. Get the abstraction wrong, and it will cause you a world of pain.

Summary

Abstraction is an important concept in software engineering separating what from how. Abstraction is one of the main concepts that allows us to create a modular design for ease of future maintenance.

However, the right abstraction is not always easy or obvious. Don’t force abstractions too early. It may take a few examples before the abstractions emerge from the domain, design or implementation.

This Abstraction series contines with:

References

Comments

Previous: Prompt Engineering Patterns Continued

Next: What Are Cohesion and Coupling?

Home: Design Pattern Evangelist Blog