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:
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)); + } + + }
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:
public class FizzBuzzer implements Function<Integer, String> { @Override public String apply(Integer n) { – return null; + return “1”; } }
And refactor the test to remove duplication:
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:
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”:
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:
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:
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:
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; } }
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”; – } – – }
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:
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:
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
+ 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; + } + + }
– 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:
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
:
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. |