4

Given the set of rules explained in "Clean Code", can one really use for loops? My confusion stems form the fact that a for loop is a low-level construct per se, thus it can only be used at the very lowest level of abstraction, by the book. But then, does it mean that using the loop is forbidden in those pieces of code that belong to a higher level of abstraction, i.e., the pieces of code where functions are being called?

I'm asking because I tried to look at my code from the point of the view of "don't mix low and high level" as per the book and realised I couldn't even write a for loop. That slowed me down to a crawl (slower than my usual turtle slow coding).

3 Answers3

10

I think you refer to the principle "One level of abstraction per function" in Clean Code.

This general principle makes perfect sense: a level of abstraction is the hiding of lower levels of details, in order to master the complexity. Mixing different levels of abstraction therefore implies that your attempt to hide/encapsulate complexity of higher level is broken by the simultaneous use of lower level, which requires a clear understanding of how both levels are interrelated. So you loose benefits of encapsulation.

This has nothing to do with the value judgment that you give to different instructions. If you consider for to be low level, you could consider the same for if, while, return, function calls, etc...

So you can use the for in your design, without breaking the rule.

Remark: What could break the rule is if you would use a for loop to access a level of detail that should be hidden. For example, if you'd use in a high level function a for to iterate through an array, when you already have a higher level function to perform a search.

Christophe
  • 81,699
7

Firstly, it's worth remembering that the book Clean Code suggests guidelines rather than rules. It seems that the specific guideline referred to here is the recommendation that you Don't mix levels of abstraction.

It's worth remembering that this advice is specifically referring to class interface design. (i.e. public/protected methods, public properties - anything which is exposed to something which uses a class)

Consider a high-level abstract class Animal, from which a low-level concrete class Dog class is derived. The Dog may include a method called Bark(), which would seem appropriate for Dog's level of abstraction..

However, there is nothing to stop the Bark() method being included in the interface of the abstract Animal class; but if other classes derived from Animal could not make use of the Bark() method then this would be a code smell. Anything derived from Animal is forced to inherit (and even override) the Bark() method, even if a Bark() method is wholly inappropriate to the concrete class.

i.e.

public abstract class Animal
{
     public abstract void Bark();
}

public class Elephant : Animal
{
     public override void Bark()
     {
         // HUH?!
     }
}

The advice is generally encouraging you to think carefully about what methods you provide in high-level abstractions/interfaces; to avoid forcing derived ("lower level") classes to inherit, override or implement any methods or behaviour which do not logically belong to the lower level class.

Language constructs such as the for keyword are unrelated to class interfaces; and the advice regarding "mixed levels of abstraction" doesn't really make any sense with regard to method implementations.

Ben Cottrell
  • 12,133
5

Your question title is a little bit provoking, since from the text its clear, what you mean is "Are for loops allowed in the “Clean Code” set of rules, except for low-level components". So this is how I interpret your question.

I think this is IMHO a very interesting question, not so trivial as it might look at a first glance. I would not interpret this just in the light of "Clean code" by Bob Martin, but also when looking at "Clean Code" as it is advertised by people like Ralf Westphal (german reference: http://clean-code-developer.de/, try Google translate). His suggestion for what he calls IODA architecture brings the principle "don't mix low and high level" to the extreme. And indeed, in this architecture, you won't find any "for" loops (and no other control structures like conditionals) at the "higher level components" - only at the lower level. The purposes of higher level components in this architecture is exclusively for "plugging" lower level components together.

If you take this really to the extreme, you need to change the structure of every function like

  function HighLevelfunction()
      sum=0
      for i = 1 to upperLimit  logic
           sum+=LowLevelFunction1(i)
      return sum

into

  function LowLevelfunction2( func funcAsParameter)
      sum=0
      ' just for demonstration purposes, think not of reusage here
      for i = 1 to upperLimit
           sum+=funcAsParameter(i)
      return sum

Note in the first version HighLevelfunction depends on LowLevelFunction, in the second LowLevelfunction2 does not depend on any other function anymore. In the second one, you will need a "higher level" function where the call LowLevelfunction2(LowLevelFunction1) takes place - but this is simply an integration step, there will be no "for" loop at the higher level needed.

However, doing this for each and every function in a real world program this will probably become soon very impractical. I (and I guess most other programmers, too) draw the line between high level and low level functions, classes or components on a much coarser granularity. One has to find the right balance, and make sure not to write clean code just for the sake of cleanlyness. Or, to say it with the words of Ralf Westphal, "Don’t Let Cleaning-Up Go Overboard".

Glorfindel
  • 3,167
Doc Brown
  • 218,378