views:

205

answers:

8

I had a little argument, and was wondering what people out there think:

In C++ (or in general), do you prefer code broken up into many shorter functions, with main() consisting of just a list of functions, in a logical order, or do you prefer functions only when necessary (i.e., when they will be reused very many times)? Or perhaps something in between?

+13  A: 

Small functions, please

It is the conventional wisdom that smaller functions are better, and I think it's true. In fact, there is a company with an analysis tool that rates individual functions by how many decisions they make compared to the number of unit tests that they have.

The theory is that you may or may not be able to reduce complexity in an entire application, but you have complete control over how much complexity is in any given function.

A measurement called cyclomatic complexity is thought to correlate positively with bad code...specifically, the more paths there are through a method the higher its CCN number is, the more poorly it is written, the harder it is to understand and hence change or even get right to start with, and the more unit tests it will need.

Ok, found the tool. It is called, ahem, the Change Risk Analysis and Predictions index.

Lately, the principle of encoding information only once has grown new acronyms, specifically DRY (Don't Repeat Yourself) and DIE (Duplication is Evil) ... I believe we can in part thank the RoR community for promoting this philosophy...

DigitalRoss
That analysis is called Cyclomatic Complexity. http://en.wikipedia.org/wiki/Cyclomatic_complexity
GMan
Heh, I was just typing that in...and looking for the measurement tool
DigitalRoss
Yup. Independent of that analysis, I figured out 20+ years ago that big functions are very hard for mere mortals to understand, write without bugs, and maintain. So my rule of thumb has been that if a function is so long that printing it in a legible font won't fit on a letter-size sheet of paper, it needs to be broken up. There are exceptions, of course.
Bob Murphy
+1 for mentioning the cyclomatic complexity.
fco.javier.sanz
+2  A: 

I think the only answer is something in between. If you break up functions every time possible, it becomes unreadable. Likewise, if you never break up, it also becomes unreadable.

I like to group functions into semantic differences. It is one logical unit for some calculation. Keep the units small, but big enough to actually do something useful.

carl
This is also what Eric Raymond argues in `The Art of Unix Programming'
guns
A: 

Whatever helps with code reuse and readability works best, I believe.

Making lots of one line functions just to do it doesn't help with readability so they should be grouped in classes that makes sense, and then split up the functions so that you can understand quickly what is going on in that function.

If you have to jump all over to understand what is going on then your design is flawed.

James Black
+3  A: 

My favorite granularity rule of thumb for a function is "no more than 24 lines of < 80 characters each" -- and that's not just because 80 x 24 terminals were all the rage "back when I started"; I think it's a reasonable guideline for functions you can "grasp as one eyeful", at least in C or languages not much richer than C. "A function does only one thing", AKA "a function has one function" (playing on the meaning of "function" as "role" or "purpose"!-) is a secondary rule I use in languages where "too much functionality" can easily be packed in 24 lines. But the "lexical eyeful" guideline -- 24 x 80 -- is still my main one.

Alex Martelli
Of course 80x24 in Python could be an entire app!
Martin Beckett
@mgb, sure, and a _couple_ of apps in Perl, where conciseness is praised over readability;-) -- but, that's where the secondary rule comes in -- "just one [function / role / purpose]".
Alex Martelli
In APL or J it's an operating system...
Steve Jessop
A: 

I prefer functions (or methods) which fit within one screenful of code, so I can see at a glance anything I need to reference to understand how that function works. I generally have about 50 lines of space in my editor windows, which are also generally at 80 columns so I can fit two side by side on a monitor and cross reference between two pieces of code.

So, I generally consider 50 lines to be about the maximum. The only time I would consider allowing more is when you have one big long initialization function or something that is completely linear (no variables, conditionals, or loops), since that's not something where you need all that much context and some APIs require a whole bunch of initialization to get up and running, and splitting it into smaller functions wouldn't really help much.

On the whole, though, nice, small, easy to understand functions that do one thing and are well named are vastly preferable to big sprawling monstrosities that are hundreds of lines long and dozens of variables to keep track of with indentation going 10 levels deep.

Brian Campbell
A: 

Another simple reason: A function should be made when a block of code is being reused more than once or twice. For very small bits of code (say one or two statements), macros often alleviate the problem.

Nick Bedford
+3  A: 

Split the functions, but never split functionality.

Functionality may be classified into layers, then each layer may split into different functions. For example, when we are processing a sine series, the main loop for summing and subtracting should be in primary function. This may consider as layer 1. Now the functionality for finding power may classified in to layer 2. This can be implemented as a sub function. Similarly finding factorial also belongs to layer 2 which would be another sub function. Always consider functionality, never count number of lines. Number of lines may vary from 3 to 300, doesn't matter. This will add more readability and maintainability to our code. This is my idea about splitting.

Vadakkumpadath
+1  A: 

Small functions are good and smaller ones are better.

About five to eight lines of code is my upper limit on function size. Beyond that, and it's too complicated. You should be able to:

  1. Assume that a callee does what its name would indicate,
  2. Read a function's definition in a matter of seconds, and
  3. Convince yourself quickly that the first assumption implies that the present function is correct.

The other thing is that you should use your functions BEFORE you write their code. When you see how you intend to use the function, then you'll see what pre- and post-conditions said functions must respect.

Anything that isn't obviously correct at first glance should be proven correct in the running commentary. If that's difficult, factor sub-functions out.

Ian