Functional FizzBuzz Kata in Java
A while ago I solved the FizzBuzz kata using Java 8 streams and lambdas. While the end result was functional, the intermediate steps were not. Surely I can do better.
As always, let’s start with a failing test:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 | package remonsinnema.blog.fizzbuzz; + + import static org.junit.Assert.assertEquals; + + import org.junit.Test; + + + public class WhenFunctionallyFuzzingAndBuzzing { + + private final FizzBuzzer fizzBuzzer = new FizzBuzzer(); + + @Test + public void shouldReplaceMultiplesOfThreeWithFizzAndMultiplesOfFiveWithBuzz() { + assertEquals(“ 1 ”, “ 1 ”, fizzBuzzer.apply( 1 )); + } + + } |
01 02 03 04 05 06 07 08 09 10 11 12 13 | package remonsinnema.blog.fizzbuzz; + + import java.util.function.Function; + + + public class FizzBuzzer implements Function<Integer, String> { + + @Override + public String apply(Integer n) { + return null ; + } + + } |
Note that I start off on a functional course right away, using Java’s Function
.
I fake the implementation to make the test pass:
1 2 3 4 5 6 7 | public class FizzBuzzer implements Function<Integer, String> { @Override public String apply(Integer n) { – return null ; + return “ 1 ”; } } |
And refactor the test to remove duplication:
01 02 03 04 05 06 07 08 09 10 11 | public class WhenFunctionallyFuzzingAndBuzzing { @Test public void shouldReplaceMultiplesOfThreeWithFizzAndMultiplesOfFiveWithBuzz() { – assertEquals(“ 1 ”, “ 1 ”, fizzBuzzer.apply( 1 )); + assertFizzBuzz(“ 1 ”, 1 ); + } + + private void assertFizzBuzz(String expected, int value) { + assertEquals(Integer.toString(value), expected, fizzBuzzer.apply(value)); } } |
Then I add another test to generalize the implementation:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 | public class WhenFunctionallyFuzzingAndBuzzing { @Test public void shouldReplaceMultiplesOfThreeWithFizzAndMultiplesOfFiveWithBuzz() { assertFizzBuzz(“ 1 ”, 1 ); + assertFizzBuzz(“ 2 ”, 2 ); } private void assertFizzBuzz(String expected, int value) { public class FizzBuzzer implements Function<Integer, String> { @Override public String apply(Integer n) { – return “ 1 ”; + return Integer.toString(n); } } |
OK, pretty standard stuff so far. Next I need to replace 3 with “Fizz”:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 | public class WhenFunctionallyFuzzingAndBuzzing { public void shouldReplaceMultiplesOfThreeWithFizzAndMultiplesOfFiveWithBuzz() { assertFizzBuzz(“ 1 ”, 1 ); assertFizzBuzz(“ 2 ”, 2 ); + assertFizzBuzz(“Fizz”, 3 ); } nbsp; private void assertFizzBuzz(String expected, int value) { public class FizzBuzzer implements Function<Integer, String> { @Override public String apply(Integer n) { – return Integer.toString(n); + return numberReplacerFor(n).apply(n); + } + + private Function<Integer, String> numberReplacerFor(Integer n) { + return n == 3 + ? i -> “Fizz” + : i -> Integer.toString(i); } } |
Here I recognize that I need to apply one of two functions, depending on the input. This code works, but needs some cleaning up. First, as a stepping stone, I extract the lambdas into fields:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 | import java.util.function.Function; public class FizzBuzzer implements Function<Integer, String> { + private final Function<Integer, String> replaceNumberWithStringRepresentation + = n -> Integer.toString(n); + private final Function<Integer, String> replaceNumberWithFizz + = n -> “Fizz”; + @Override public String apply(Integer n) { return numberReplacerFor(n).apply(n); private Function<Integer, String> numberReplacerFor(Integer n) { return n == 3 – ? i -> “Fizz” – : i -> Integer.toString(i); + ? replaceNumberWithFizz + : replaceNumberWithStringRepresentation; } } |
Next I emphasize that “3” and “Fizz” go together by extracting a class:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | public class FizzBuzzer implements Function<Integer, String> { private final Function<Integer, String> replaceNumberWithStringRepresentation = n -> Integer.toString(n); – private final Function<Integer, String> replaceNumberWithFizz – = n -> “Fizz”; + private final Fizzer replaceNumberWithFizz = new Fizzer(); @Override public String apply(Integer n) { } private Function<Integer, String> numberReplacerFor(Integer n) { – return n == 3 + return replaceNumberWithFizz.test(n) ? replaceNumberWithFizz : replaceNumberWithStringRepresentation; } + package remonsinnema.blog.fizzbuzz; + + import java.util.function.Function; + import java.util.function.Predicate; + + + public class Fizzer implements Function<Integer, String>, Predicate<Integer> { + + @Override + public boolean test(Integer n) { + return n == 3 ; + } + + @Override + public String apply(Integer n) { + return “Fizz”; + } + + } |
Here I’m using the standard Java Predicate
functional interface.
To add “Buzz”, I need to generalize the code from a single if
(hidden as the ternary operator) to a loop:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | public class WhenFunctionallyFuzzingAndBuzzing { assertFizzBuzz(“ 1 ”, 1 ); assertFizzBuzz(“ 2 ”, 2 ); assertFizzBuzz(“Fizz”, 3 ); + assertFizzBuzz(“ 4 ”, 4 ); + assertFizzBuzz(“Buzz”, 5 ); } private void assertFizzBuzz(String expected, int value) { package remonsinnema.blog.fizzbuzz; + import java.util.Arrays; + import java.util.Collection; import java.util.function.Function; private final Function<Integer, String> replaceNumberWithStringRepresentation = n -> Integer.toString(n); – private final Fizzer replaceNumberWithFizz = new Fizzer(); + private final Collection<ReplaceNumberWithFixedText> replacers = Arrays.asList( + new ReplaceNumberWithFixedText( 3 , “Fizz”), + new ReplaceNumberWithFixedText( 5 , “Buzz”) + ); @Override public String apply(Integer n) { } private Function<Integer, String> numberReplacerFor(Integer n) { – return replaceNumberWithFizz.test(n) – ? replaceNumberWithFizz – : replaceNumberWithStringRepresentation; + for (ReplaceNumberWithFixedText replacer : replacers) { + if (replacer.test(n)) { + return replacer; + } + } + return replaceNumberWithStringRepresentation; } } |
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 | package remonsinnema.blog.fizzbuzz; – – import java.util.function.Function; – import java.util.function.Predicate; – – – public class Fizzer implements Function<Integer, String>, Predicate<Integer> { – – @Override – public boolean test(Integer n) { – return n == 3 ; – } – – @Override – public String apply(Integer n) { – return “Fizz”; – } – – } |
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | package remonsinnema.blog.fizzbuzz; + + import java.util.function.Function; + import java.util.function.Predicate; + + + public class ReplaceNumberWithFixedText implements Function<Integer, String>, + Predicate<Integer> { + + private final int target; + private final String replacement; + + public ReplaceNumberWithFixedText( int target, String replacement) { + this .target = target; + this .replacement = replacement; + } + + @Override + public boolean test(Integer n) { + return n == target; + } + + @Override + public String apply(Integer n) { + return replacement; + } + + } |
Oops, old habits… That should be a stream rather than a loop:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 | import java.util.function.Function; public class FizzBuzzer implements Function<Integer, String> { – private final Function<Integer, String> replaceNumberWithStringRepresentation + private final Function<Integer, String> defaultReplacer = n -> Integer.toString(n); private final Collection<ReplaceNumberWithFixedText> replacers = Arrays.asList( new ReplaceNumberWithFixedText( 3 , “Fizz”), } private Function<Integer, String> numberReplacerFor(Integer n) { – for (ReplaceNumberWithFixedText replacer : replacers) { – if (replacer.test(n)) { – return replacer; – } – } – return replaceNumberWithStringRepresentation; + return replacers.stream() + .filter(replacer -> replacer.test(n)) + .map(replacer -> (Function<Integer, String>) replacer) + .findFirst() + .orElse(defaultReplacer); } } |
Much better. The next test is for multiples:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 | public class WhenFunctionallyFuzzingAndBuzzing { assertFizzBuzz(“Fizz”, 3 ); assertFizzBuzz(“ 4 ”, 4 ); assertFizzBuzz(“Buzz”, 5 ); + assertFizzBuzz(“Fizz”, 6 ); } private void assertFizzBuzz(String expected, int value) { public class FizzBuzzer implements Function<Integer, String> { private final Function<Integer, String> defaultReplacer = n -> Integer.toString(n); – private final Collection<ReplaceNumberWithFixedText> replacers = Arrays.asList( – new ReplaceNumberWithFixedText( 3 , “Fizz”), – new ReplaceNumberWithFixedText( 5 , “Buzz”) + private final Collection<ReplaceMultipleWithFixedText> replacers = Arrays.asList( + new ReplaceMultipleWithFixedText( 3 , “Fizz”), + new ReplaceMultipleWithFixedText( 5 , “Buzz”) ); @Override |
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | + package remonsinnema.blog.fizzbuzz; + + import java.util.function.Function; + import java.util.function.Predicate; + + + public class ReplaceNumberWithFixedText implements Function<Integer, String>, + Predicate<Integer> { + + private final int target; + private final String replacement; + + public ReplaceNumberWithFixedText( int target, String replacement) { + this .target = target; + this .replacement = replacement; + } + + @Override + public boolean test(Integer n) { + return n % target == 0 ; + } + + @Override + public String apply(Integer n) { + return replacement; + } + + } |
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | – package remonsinnema.blog.fizzbuzz; – – import java.util.function.Function; – import java.util.function.Predicate; – – – public class ReplaceNumberWithFixedText implements Function<Integer, String>, Predicate<Integer> { – – private final int target; – private final String replacement; – – public ReplaceNumberWithFixedText( int target, String replacement) { – this .target = target; – this .replacement = replacement; – } – – @Override – public boolean test(Integer n) { – return n == target; – } – – @Override – public String apply(Integer n) { – return replacement; – } – – } |
The last test is to combine Fizz and Buzz:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | public class WhenFunctionallyFuzzingAndBuzzing { assertFizzBuzz(“ 4 ”, 4 ); assertFizzBuzz(“Buzz”, 5 ); assertFizzBuzz(“Fizz”, 6 ); + assertFizzBuzz(“ 7 ”, 7 ); + assertFizzBuzz(“ 8 ”, 8 ); + assertFizzBuzz(“Fizz”, 9 ); + assertFizzBuzz(“Buzz”, 10 ); + assertFizzBuzz(“ 11 ”, 11 ); + assertFizzBuzz(“Fizz”, 12 ); + assertFizzBuzz(“ 13 ”, 13 ); + assertFizzBuzz(“ 14 ”, 14 ); + assertFizzBuzz(“FizzBuzz”, 15 ); } private void assertFizzBuzz(String expected, int value) { package remonsinnema.blog.fizzbuzz; import java.util.Arrays; import java.util.Collection; import java.util.function.Function; + import java.util.stream.Collectors; + import java.util.stream.Stream; public class FizzBuzzer implements Function<Integer, String> { @Override public String apply(Integer n) { – return numberReplacerFor(n).apply(n); + return numberReplacersFor(n) + .map(function -> function.apply(n)) + .collect(Collectors.joining()); } – private Function<Integer, String> numberReplacerFor(Integer n) { – return replacers.stream() + private Stream<Function<Integer, String>> numberReplacersFor(Integer n) { + return Stream.of(replacers.stream() .filter(replacer -> replacer.test(n)) .map(replacer -> (Function<Integer, String>) replacer) .findFirst() – .orElse(defaultReplacer); + .orElse(defaultReplacer)); } } |
I generalized the single Function
into a Stream
of Function
s, to which I apply the Map-Reduce pattern. I could have spelled out the Reduce part using something like .reduce("", (a, b) -> a + b)
, but I thinkCollectors.joining()
is more expressive.
This doesn’t pass the test yet, since I return a stream of a single function. The fix is a little bit tricky, because I need to know whether any applicable replacer functions were found, and you can’t do that without terminating the stream. So I need to create a new stream using StreamSupport
:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | package remonsinnema.blog.fizzbuzz; import java.util.Arrays; import java.util.Collection; + import java.util.Iterator; + import java.util.Spliterators; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; + import java.util.stream.StreamSupport; public class FizzBuzzer implements Function<Integer, String> { } private Stream<Function<Integer, String>> numberReplacersFor(Integer n) { – return Stream.of(replacers.stream() + Iterator<Function<Integer, String>> result = replacers.stream() .filter(replacer -> replacer.test(n)) .map(replacer -> (Function<Integer, String>) replacer) – .findFirst() – .orElse(defaultReplacer)); + .iterator(); + return result.hasNext() + ? StreamSupport.stream(Spliterators.spliteratorUnknownSize(result, 0 ), false ) + : Stream.of(defaultReplacer); } } |
And that’s it. The full code is on GitHub.
I learned two lessons from this little exercise:
- Java comes with a whole bunch of functional interfaces, like
Function
andPredicate
, that are easily combined with streams to solve a variety of problems. - The standard
if → while
transformation becomesif → stream
in the functional world.
Reference: | Functional FizzBuzz Kata in Java from our JCG partner Remon Sinnema at the Secure Cloud Development blog. |