This is not a challenge so much as a new (to me) way of looking at refactoring. As always, complete in your language of choice. First, some background:
The ruby example below maximizes simplicity at the expense of flexibility.
class Bottles
def sing
verses(99, 0)
end
def verses(upper_bound, lower_bound)
upper_bound.downto(lower_bound).map {|n| verse(n) + "\n"}.join
end
def verse(number)
case number
when 0
"No more bottles of beer on the wall, no more bottles of beer.\nGo to the store and buy some more, 99 bottles of beer on the wall.\n"
when 1
"1 bottle of beer on the wall, 1 bottle of beer.\nTake it down and pass it around, no more bottles of beer on the wall.\n"
when 2
"2 bottles of beer on the wall, 2 bottles of beer.\nTake one down and pass it around, 1 bottle of beer on the wall.\n"
else
"#{number} bottles of beer on the wall, #{number} bottles of beer.\nTake one down and pass it around, #{number-1} bottles of beer on the wall.\n"
end
end
end
We have a change request, though. When there are 6 bottles of beer left, we will need to display "1 six pack" instead of "6 bottles" in the song. The challenge is not to implement this change yet. We're going to Kent Beck this shit up.
for each desired change, make the change easy (warning: this may be hard), then make the easy change
— Kent Beck (@KentBeck) September 25, 2012
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
https://twitter.com/KentBeck/status/250733358307500032
Ultimately we want to make the Bottles
class open closed to the type of change that would allow us to arbitrarily say 1 six pack instead of 6 bottles. (I had to ask what open closed meant in this context in class.) Open closed here means open to the change but closed to modification. That's going to be too much for a single refactor in the style I'm about to describe so please allow me to enumerate what I would like for the next submission.
###Challenge Goal
- No hard coded strings left in the
verse
method.
###Refactor Under Green Restrictions
- Tests may never go red.
- Tests must be run at every file save.
- Try to save every time you make a change. Only change 2+ "things" without saving if you're stuck.
- If your tests go red, hit undo until they're back to green.
###The Refactor Under Green Process
- Look for similar structures.
- Look for the smallest possible change that can be made to make them look more similar.
- Change your system so making that change is easy. (Almost always additive. Tests can't go red, right?)
- Make the easy change.
- Delete dead code.
Refactor Example
File change and tests run between each code block.
when 2
andelse
seem to be almost identical already, let's tackle that first. Using thenumber
variable instead of hard coded 2 seems to be the lowest hanging fruit.The pluralization of bottles is the next issue.
Lets start calling the container method to make sure it's for reals.
Maybe I should install nodemon to automatically run my tests...
Time for the actual guts.
Now we remove the old
else
line and make sure the tests still pass.Now I'll copy our new else up to
when 2
(and not return it) because I don't feel like thinking. This will let me know if it blows up without me needing to worry about its return value yet.I could delete the old
when 2
return value to make sure our new line is returning the right thing. Instead I'll just delete the entirewhen 2
statement since we're making a change to be identical toelse
anyway.Now to try and make
when 1
andelse
identical.