Roy Osherove's TDD Kata: My first attempt
I recently came across Roy Osherove’s commentary on Corey Haines’ attempt at Roy’s TDD Kata so I thought I’d try it out in C#.
Andrew Woodward has recorded his version of the kata where he avoids using the mouse for the whole exercise so I tried to avoid using the mouse as well and it was surprisingly difficult!
I’ve only done the first part of the exercise so far which is as follows:
I know a lot of the Resharper shortcuts but I found myself using the mouse mostly to switch to the solution explorer and run the tests.
These are some of the shortcuts that have become more obvious to me from trying not to use the mouse:
The first run through of the exercise I made use of a guard block for the empty string case and then went straight to ‘String.Split’ to get each of the numbers and then add them together.
It annoyed me that there had to be a special case for the empty string so I changed my solution to make use of a regular expression instead:
Regex.Matches(numbers, "\\d").Cast<Match>().Select(x => int.Parse(x.Value)).Aggregate(0, (acc, num) => acc + num);
That works for nearly all of the cases provided but it’s not incremental at all and it doesn’t even care if there are delimeters between each of the numbers or not, it just gets the numbers!
It eventually came unstuck when trying to work out if there were negative numbers or not. I considered trying to work out how to do that with a regular expression but it did feel as if I’d totally missed the point of the exercise:
I decided to watch Corey’s video to see how he’d achieved this and I realised he was doing much smaller steps than me.
I started again following his lead and found it interesting that I wasn’t naturally seeing the smallest step but more often than not the more general solution to a problem.
For example the first part of the problem is to add together two numbers separated by a comma.
Given an input of “1,2” we should get a result of 3.
I really wanted to write this code to do that:
if(number == "") return 0; return number.Split(',').Aggregate(0, (acc, num) => acc + int.Parse(num));
But a simpler version would be this (assuming that we’ve already written the code for handling a single number):
if (number == "") return 0; if (number.Length == 1) return int.Parse(number); return int.Parse(number.SubString(0,1)) + int.Parse(number.SubString(2, 1));
After writing a few more examples we do eventually end up at something closer to that first solution.
I’m normally a fan of doing simple incremental steps but for me the first solution expresses the intent of our solution much more than the second one does and the step from using ‘SubString’ to using ‘Split’ doesn’t seem that incremental to me. It’s a bit of a leap.
This exercise reminds me a bit of a post by Reg Braithwaite where he talks about programming golf. In this post he makes the following statement:
In the second version of this we’re describing the relationship very specifically and then we’ll generalise that relationship later when we have an example which forces us to do that. I think that’s a good thing that the incremental approach encourages.
In this exercise I found that the biggest benefit of only coding what you needed was that the code was easier to change when a slightly different requirement was added. If we’ve already generalised our solution then it can be quite difficult to add that new requirement.
I recently read a post by Matt Podwysocki where he talks about three different types of programming:
From my experience generalising code prematurely hurts us the most when we’re programming in the large/medium and it’s really difficult to recover once we’ve done that.
I’m not so sure where the line is when programming in the small. I feel like generalising code inside small functions is not such a bad thing although based on this experience perhaps that’s me just trying to justify my currently favoured approach!