5

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

17

3 Answers 3

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.

4
  • I guess you are on the wrong track, all control structures can be seen as low level constructs, and if ones takes to the extreme, this would indeed mean not to use for on the higher levels. See my answer why.
    – Doc Brown
    Mar 17, 2016 at 12:00
  • To be honest, I don't know if my track is right or yours is wrong. But one shall not confuse a fundamental language structure with a low level structure. Let's make an analogy: take any high level philosophical text of your favorite author about the sense of life, a text full of abstract words: only high level concepts. Such text will certainly not bother us with low level details such as atoms and quarks. But it will certainly use prepositions, articles, conjunctions and other fundamental language structures. Will you really get the impression of mixing levels of abstraction then ?
    – Christophe
    Mar 17, 2016 at 20:49
  • Well, we can agree upon the "One level of abstraction per function" rule is not about forbidding control structures on higher abstraction levels. But my point is that this this rule does not cover what "separation of concerns" will cause for high and low level constructs when thought to an end. When "high level constructs" will exclusively be used for integration of lower level components, and only lower level components contain "business logic", in the end that means there will be no control structures at the higher level components....
    – Doc Brown
    Mar 17, 2016 at 21:42
  • 1
    But don't get me wrong: I am not saying that is the only possible or right way of writing "clean code". It is just one interpretation, maybe a little bit extreme, but actually a correct interpretation the OP came up by himself by thinking about what the clean code rules might imply. And indeed, I think it is possible to create real-world programs that way (maybe "more cleaner than anyone needs").
    – Doc Brown
    Mar 17, 2016 at 21:44
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.

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

2
  • I think the take-away is that LowLevelfunction2 now really is sum and therefore a reusable algorithm. The C++ community has taken this kind of algorithm extraction quite far which I believe is a good thing. Indeed, I would consider an explicit loop to compute a sum in a function that has other business than just computing a sum a code smell.
    – 5gon12eder
    Mar 17, 2016 at 11:06
  • @5gon12eder: you comment shows me my example was not well choosen to show my point. This question is about clean code, functions for abstractions, not for reuse, and the question was if this kind of decoupling should be done always, not just when there is a possible resuage scenario. I will edit the question to make this more clear.
    – Doc Brown
    Mar 17, 2016 at 11:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.