Masters ... are zealots of practice, connoisseurs of the small, incremental step.
I was doing the roman numerals kata in ruby and something related to small incremental steps occurred to me. Here's three tests:
def test_to_roman assert_equal "I", to_roman(1) assert_equal "II", to_roman(2) assert_equal "III", to_roman(3) end
and here's the first code snippet:
def to_roman(n) roman = '' if n == 3 roman = 'III' end if n == 2 roman = 'II' end if n == 1 roman = 'I' end roman end
I can refactor these three if statements into a single while statement, in very small steps, as follows:
def to_roman(n) roman = '' if n >= 3 roman += 'I' n -= 1 end if n >= 2 roman += 'I' n -= 1 end if n >= 1 roman += 'I' n -= 1 end roman end
and then:
def to_roman(n) roman = '' if n >= 1 roman += 'I' n -= 1 end if n >= 1 roman += 'I' n -= 1 end if n >= 1 roman += 'I' n -= 1 end roman end
and then:
def to_roman(n) roman = '' while n >= 1 roman += 'I' n -= 1 end roman end
Ok. Now here's the original again:
def to_roman(n) roman = '' if n == 3 roman = 'III' end if n == 2 roman = 'II' end if n == 1 roman = 'I' end roman end
And this time I refactor in a different direction, towards an array lookup:
def to_roman(n) units = [ '', 'I', 'II', 'III' ] roman = '' if n == 3 roman = units[3] end if n == 2 roman = units[2] end if n == 1 roman = units[1] end roman end
And then:
def to_roman(n) units = [ '', 'I', 'II', 'III' ] roman = '' if n == 3 roman = units[n] end if n == 2 roman = units[n] end if n == 1 roman = units[n] end roman end
And then:
def to_roman(n) units = [ '', 'I', 'II', 'III' ] roman = '' if n == 3 roman = units[n] end if n == 2 roman = units[n] end if n == 1 roman = units[n] end roman units[n] end
And then:
def to_roman(n) units = [ '', 'I', 'II', 'III' ] units[n] end
In this case I can refactor from a sequence of if statements in two directions equally easy - towards a while or towards an array lookup. Ok. Now consider the situation if I was starting from a switch instead of a sequence of ifs:
def to_roman(n) case n when 3 then 'III' when 2 then 'II' when 1 then 'I' end end
From this I can easily refactor towards an array lookup but refactoring towards a while is perhaps not quite so straightforward.
I'm suggesting that a construct is useful not just in it's own right but also in relation to all the other constructs it might get refactored to or from. How easily can I move from one construct to another? Do some constructs live only on one-way roads? Do some lead you down more dead-ends than others?
I'm reminded of the family on holiday in the West Country who got thoroughly lost. Spotting a farmer leaning on a gate they stop the car, wind down the window and ask
Excuse me. Can you tell me how to get to Nempnett Thrubwell?
The old farmer looks at them and says
Well.... you don't want to start from here.