Some houses are populated only with teenagers. When one gets in the shower they notice the plug-hole is clogged with horrible gunk: hairs of indeterminate origin, bits of soap, spit, bogies, general fluff (like the stuff that grows in your belly button), congealed shower gel, etc., etc.,. But they don't clean out the plug-hole. After all, they didn't make the mess. When they've finished they're cleaner. But only slightly. And they don't clean the plug-hole. Partly because they then notice there's no towel. They curse the other teenagers as they walk around the house naked, dripping water as they go, looking for anything vaguely absorbant they can commandeer.
After a while the plug-hole gets so gunked up tackling it becomes a truly repulsive task. It actually clogs the plug-hole. The shower fills with water - it starts turning into a bath! But it wasn't designed as a bath. Splashes leak over the lip of the door. Water collects under the shower base where it's not visible. Not at first anyway. After a while the ceiling below the shower starts to discolour in a tell-tale circular pattern. But it goes unnoticed. At first anyway. Soon the ring grows so large it's unmissable. But it's ignored. After all, it's a full-scale call-the-plumber-and-tiler job now.
Some houses contain a mixture of teenagers and adults.
When a teenager showers sometimes the plug-hole is gunk free and there's a dry towel but sometimes the plug-hole is gunked up and there's no dry towel. They can't quite understand this. But they never clean the plug-hole and never put up a clean dry towel.
When an adult showers sometimes there's a build-up of plug-hole-gunk and no dry towel. They know the previous shower-goer was a teenager.
Sometimes there's no plug-hole-gunk and there is a dry towel. They know the previous shower-goer was an adult.
When they've finished they clean out any plug-hole-gunk and put up a new dry towel.
Some houses house only responsible adults. Whenever they start to shower they don't notice any gunk around the plug-hole because there never is any. And there's always a fresh clean towel.
After they've showered they clear any plug-hole gunk and put up a new dry towel.
Hi. I'm Jon Jagger, director of software at Kosli.
I built cyber-dojo, the place teams practice programming.
Showing posts with label slime. Show all posts
Showing posts with label slime. Show all posts
sliming and refactoring and deliberate duplication
Suppose I'm doing the Print-Diamond kata in Ruby:
Given a letter print a diamond starting with 'A'
with the supplied letter at the widest point.
For example: print-diamond 'E' prints
A
B B
C C
D D
E E
D D
C C
B B
A
I start with a test
def test_diamond_A
assert_equal ['A'], diamond('A')
end
which I pass using
def diamond(widest) ['A'] endI add another test
def test_diamond_B
assert_equal [' A',
'B B',
' A'], diamond('B')
end
which I pass using
def diamond(widest)
if widest == 'A'
return ['A']
end
if widest == 'B'
return [' A',
'B B',
' A']
end
end
I add one more test
def test_diamond_C
assert_equal [' A',
' B B',
'C C',
' B B',
' A'], diamond('C')
end
which I pass using
def diamond(widest)
if widest == 'A'
return ['A']
end
if widest == 'B'
return [' A',
'B B',
' A']
end
if widest == 'C'
return [' A',
' B B',
'C C',
' B B',
' A']
end
end
The tests have already proved valuable:
- I've decided I don't want to actually test printing
- I've chosen the result format - an array of strings
- I've chosen not to embed newlines at the end of the strings
- I've something to refactor against
While coding the array of strings for the 'C' case I found myself copying the result for 'B' and modifying that. Specifically, I had to:
- duplicate the 'B B' string
- add a space at the start of the ' A' and 'B B' strings
- add a new middle string 'C C'
def diamond(widest)
d = inner_diamond(widest)
mid = d.length / 2
d[0..mid-1] + d[mid+1..-1]
end
def inner_diamond(widest)
if widest == 'A'
return ['A',
'A']
end
if widest == 'B'
return [' A',
'B B',
'B B',
' A']
end
if widest == 'C'
return [' A',
' B B',
'C C',
'C C',
' B B',
' A']
end
end
This looks a promising step towards a recursive solution - to make the implementation of 'C' contain the implementation of 'B' and then add strings only for 'C'. So, remembering what I had to do when copying and modifying, I refactored to this:
def inner_diamond(widest)
if widest == 'A'
return ['A',
'A']
end
if widest == 'B'
return [' A',
'B B',
'B B',
' A']
end
if widest == 'C'
b = inner_diamond('B')
upper,lower = split(b.map{ |s| ' ' + s })
c = widest + ' ' + widest
return upper + [c,c] + lower
end
end
def split(array)
mid = array.length / 2
[ array[0..mid-1], array[mid..-1] ]
end
From here I verified the recursive solution works for 'B' as well:
def inner_diamond(widest)
if widest == 'A'
return ['A',
'A']
end
if widest == 'B'
a = inner_diamond('A')
upper,lower = split(a.map{ |s| ' ' + s })
b = widest + ' ' + widest
return upper + [b,b] + lower
end
if widest == 'C'
b = inner_diamond('B')
upper,lower = split(b.map{ |s| ' ' + s })
c = widest + ' ' + widest
return upper + [c,c] + lower
end
end
Now I worked on generalizing the use of the hard-coded argument to inner_diamond() and the hard-coded number of spaces:
def inner_diamond(widest)
if widest == 'A'
return ['A','A']
end
if widest == 'B'
a = inner_diamond(previous(widest))
upper,lower = split(a.map{ |s| ' ' + s })
n = (widest.ord - 'A'.ord) * 2 - 1
b = widest + (' ' * n) + widest
return upper + [b,b] + lower
end
if widest == 'C'
b = inner_diamond(previous(widest))
upper,lower = split(b.map{ |s| ' ' + s })
n = (widest.ord - 'A'.ord) * 2 - 1
c = widest + (' ' * n) + widest
return upper + [c,c] + lower
end
end
def previous(letter)
(letter.ord - 1).chr
end
Now I collapsed the duplicated specific code to its more generic form:
def inner_diamond(widest)
if widest == 'A'
return ['A','A']
else
a = inner_diamond(previous(widest))
upper,lower = split(a.map{ |s| ' ' + s })
n = (widest.ord - 'A'.ord) * 2 - 1
b = widest + (' ' * n) + widest
return upper + [b,b] + lower
end
end
Finally some renaming:
def inner_diamond(widest)
if widest == 'A'
return ['A','A']
else
inner = inner_diamond(previous(widest))
upper,lower = split(inner.map{ |s| ' ' + s })
n = (widest.ord - 'A'.ord) * 2 - 1
middle = widest + (' ' * n) + widest
return upper + [middle,middle] + lower
end
end
To summarise:
- When sliming I try to think ahead and choose tests which allow me to unslime the slime.
- If I have slimed 3 times, my next step should be to unslime rather than adding a 4th gob of slime.
- My first unsliming step is often deliberate duplication, done in a way that allows me to collapse the duplication.
Subscribe to:
Posts (Atom)
