Monday, November 19, 2007

Detestable long methods

Some people like long methods. Some do not like them. Personally I belong to the latter category. But that does not really matter here. Most discussions around the subject fall back on some vague aesthetic arguments. Usually it ends in some variant of “I like to see what happens” vs “I think we should build nice abstractions”. It is always nice to try to fall back onto something measurable.

Even if I might not make it all the way to strict mathematical scientific precision, I want to throw in an argument that is just a little bit less fuzzy. Let’s talk about test, baby …

Say that we have a decently long method. Probably it will take care of a few things, taking responsibility of A, B and C. This could be respectively validating some data, doing the right thing on validation error and processing it in different ways. For each of these we have a few different variants (m,n, resp p).

To test this code we need to create a test for every path through the method. As we have m alternatives for A, n alternatives for B, and p alternatives for C we will probably need about m*n*p tests. This is roughly the same as the cyclomatic complexity.

Now, let’s do some refactoring. Responsibility A is split out to a method of its own, B to a method of its own, and C likewise. What remains is of course the same old method as before, but it now just consists of a few calls to a(), b(), and c().

To test a() in isolation we will need about m different test cases. To test b() we need n, and to test c() we need p. Then we need a test that puts them all together and covers the (previously) long method. Might take a few, but say one for simplicity. In total we need m+n+p+1 tests for full coverage.

So, by separation of concerns we have limited the number of test cases for full path coverage from m*n*p to m+n+p, which probably is drastically less. Put another way: our refactoring imploded the combinatorial explosion into linear space.

Ok, agreed that the analysis is a bit rough and not really rigid; but I think the general idea holds. If someone wants to elaborate to an example and do a strict analysis, let me know.

As for “detestable” I like to give the credit to the unknown person of the Denver JUG (if I’m not misinformed) who coined the expression: darn good job.