Monday, January 29, 2018

FizzBuzz

Today I got sent to a training class at work. I'll be there for the next couple days as well. The topic is Test Driven Design (TDD), though I prefer to call it Test Driven Development because I still think there's a place in the world for mapping out your thoughts before you start cranking code. The head honcho architect at RGA wants all the architects and leads to know this stuff so, even though I've picked up most of the concepts on my own, I was enrolled. I'm fine with that, it's well taught and it never hurts to formalize your knowledge.

It's a hands-on class and the first exercise was to code the silly icebreaker game "FizzBuzz". If you've never heard of it, you go around the room counting off numbers except that if a number is divisible by 3, you say "Fizz". If it's divisible by 5, you say "Buzz". If it's divisible by both, you say "FizzBuzz". The correct answers for the first 16 are:

1, 2, Fizz, 4, Buzz, Fizz, 7, 8, Fizz, Buzz, 11, Fizz, 13, 14, FizzBuzz, 16

It's not as easy as it sounds because it's hard to keep track of the numbers when the Fizz's and Buzz's overlap. As I've stated before, whatever you think of TDD in practice, it really shines when you have super-well-defined problems like this one. We did this one using the ping-pong approach where two developers share a platform and one writes a test and then the other modifies the code to pass the test. You try to do this as quickly as possible to prevent any extra gold-plating seeping in (you can always refactor it later).

Again, when the problem is this simple, you often get a pretty good solution on the first go. Here's the final test (the preceding ones built up to this by first getting the single value right and then gluing them together):


@Test
public void speakSequence_16returns() {
  assertEquals("1, 2, Fizz, 4, Buzz, Fizz, 7, 8, Fizz, Buzz, 11, Fizz, 13, 14, FizzBuzz, 16",
  testObject.speakSequence(16));
}

And, here's the solution:

public class FizzBuzz {

  public String speak(Integer number) {
    return number %15 == 0 ? "FizzBuzz" :
      number % 5 == 0 ? "Buzz" :
      number % 3 == 0 ? "Fizz" :
      number.toString();
    }

  public String speakSequence(Integer maxNumber) {
    if (maxNumber < 1) throw new IllegalArgumentException();
    return IntStream
        .rangeClosed(1, maxNumber)
        .mapToObj(i->speak(i))
        .collect(Collectors.joining(", "));
  }
}

Ok, the IntStream is probably overkill, but it's certainly elegant. And it took the two of us less than 10 minutes to produce this fully-tested class.

No comments:

Post a Comment