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 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.
Like this:
Like Loading...
Related