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 Functions, 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 think Collectors.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 and Predicate, that are easily combined with streams to solve a variety of problems.
The standard if → while transformation becomes if → stream in the functional world.
Published
2016-08-01 2016-07-30