Mark Needham

Thoughts on Software Development

Coding: Single Level of Abstraction Principle

with 20 comments

One of the other useful principles for writing readable code that I’ve come across in the last year or so is the Single Level of Abstraction Principle.

I first came across the idea of writing code at the same level of abstraction in Uncle Bob’s Clean Code although I only learnt about the actual term in Neal Ford’s The Productive Programmer.

As the name suggests the idea is that within a certain method we look to keep all the code at the same level of abstraction to help us read it more easily. It needs to be consistent with stuff around it otherwise it’s really confusing as our brain tried to make the mental shift between thinking about higher level concepts and low level implementation details in the code.

While trying to understand the code which I wrote about in a previous post about keeping method names positive we decided to extract each of the validation rules so that we could see in English what was going on.

private bool ValidPolicyNumber(string policyNumber) 
{
	var hasExpectedPrefix = policyNumber.Substring(0,5) == "POLIC";
	var followedBy7Digits = Regex.IsMatch(policyNumber.Substring(6,7), "^[0-9]{7}$");
	var hasLengthOf12 = policyNumber.Length == 12;
 
	return hasExpectedPrefix && followedBy7Digits && hasLengthOf12;
}

Although there’s more code in that second example I think it makes it more clear what’s going on and if we’re interested in the how then we can just read the assignments to each of the variables.

After we’d done this refactoring I suggested that perhaps we could inline the ‘hasLengthOf12′ variable since it didn’t seem to be adding value as all it’s doing is abstracting away the fact that we’re calling the ‘Length’ properly on string to check the length.

The code would then read like this:

private bool ValidPolicyNumber(string policyNumber) 
{
	var hasExpectedPrefix = policyNumber.Substring(0,5) == "POLIC";
	var followedBy7Digits = Regex.IsMatch(policyNumber.Substring(6,7), "^[0-9]{7}$");
 
	return hasExpectedPrefix && followedBy7Digits && policyNumber.Length == 12;
}

Dave rightly pointed out that if we were to do this then we would be mixing code which said what made a valid policy with code that determined how we did this therefore violating the Single Level of Abstraction Principle.

The whole point of doing this for me is that we can keep less context in our head about the code and we can just read through the code and quickly understand what’s going on.

An added benefit is that from my experience it helps to bring out subtle errors that we may have looked over when there was a mixture of levels of abstraction in the code.

I know this is a fairly simple example but it helped me to understand a bit more clearly what it actually means to code to this principle.

* Update *

As Dan points out in the comments the ‘followedBy7Digits’ variable could never be true. I had the example wrong so I’ve change it to how it was meant to be.

Be Sociable, Share!

Written by Mark Needham

June 12th, 2009 at 5:35 pm

Posted in Coding

Tagged with ,

  • http://andypalmer.com Andy Palmer

    I would probably move the temporary variables into separate methods.

    ie. PrefixLooksLikeA(policyNumber) && Last7CharactersLookLikeA(policyNumber) && IsTheExpectedLengthOfA(policyNumber)

  • http://andypalmer.com Andy Palmer

    In fact, if you used a Tiny Type, you could get the PolicyNumber to validate itself.
    I think I would like that better :-)

  • Pingback: Twitted by leonardo_borges

  • Pingback: Arjan`s World » LINKBLOG for June 12, 2009

  • Alex

    The hasLengthOf12 variable adds nothing here. If hasExpectedPrefix and followedBy7Digits are both true, hasLengthOf12 will never be false.

  • http://www.markhneedham.com Mark Needham

    @Alex We were actually wondering whether it was redundant at the time but I think it was before we put the ‘$’ into the ‘followedBy7Digits’ regex!

    Good spot.

  • http://zdsbs.blogspot.com Zach Shaw

    So… I’ve been wondering this since the first post on this topic, why wasn’t ValidPolicyNumber public / protected / what ever and just TDDed? Then you would have gotten around a lot of the tricky implementation details… like is policy.number == 12 needed.

    -Zach

  • http://www.markhneedham.com Mark Needham

    Hey Zach,

    I actually came across this code in an acceptance test/end to end test so I guess then you have the debate of whether or not you should test your test code or not .

    I think you’re absolutely right that it would have been much easier if it had been TDD’s in this case though.

    My pair & I’s goal when we came across this code was to try and pull stuff out so that we could understand what was going on a bit more clearly and although it has rightly been pointed out that we don’t need all of those assertions it did help us at the time.

    Mark

  • Dan

    Perhaps this is because I’m from a Java background and don’t understand C# regex, but …

    Assuming it’s like a POSIX regex, doesn’t the ^ and $ in followedBy7Digits mean “starts with 7 characters of [0-9] and then ends”. If so, how can hasExpectedPrefix && followedBy7Digits ever be true?

    With a bit of refactoring I think you could just have one line:

    return Regex.IsMatch(policyNumber, “^POLIC[0-9]{7}$”);

  • http://www.markhneedham.com Mark Needham

    @Dan yeh you can have it simplified down to exactly what you said and yeh it can’t be true – the example is slightly wrong actually.

    It should be:

    var followedBy7Digits = Regex.IsMatch(policyNumber.Substring(6,7), “^[0-9]{7}$”);

  • Jonas Elfström

    I’m a fan of “var” as much as the next guy but what’s the reasoning behind “var hasExpectedPrefix” instead of “bool hasExpectedPrefix”? Since it only saves you from typing one single character I guess it’s some kind of code convention of yours?

  • http://www.markhneedham.com Mark Needham

    @Jonas yeh just because all the other’s are var that one’s var. I tend to have all local variables as var these days. Rarely seem to miss explicitly typing on the left hand side of assignments.

  • Pingback: Aligning the abstraction level with constant booleans « Schneide Blog

  • Pingback: • Will a single level of abstraction result in a higher complexity? | test.ical.ly

  • Pingback: Shaun Abram » Blog Archive » OSCON Day1: The Productive Programmer, part 2

  • Pingback: Border Crossings » Blog Archive » The Elements of Style

  • Pingback: Levels Of Abstraction In An MVC View

  • Pingback: Wishful Thinking & Test-Driven-Development | Sam Serpoosh Blog

  • Pingback: how to get your code submission accepted | Developer Minutes

  • Pingback: Missing or different in Python | All of the Things