Java中的功能性FizzBuzz Kata
不久前,我使用Java 8流和lambda解決了FizzBu??zz kata問題。 盡管最終結(jié)果是可行的,但中間步驟卻沒有。 我當(dāng)然可以做得更好。
與往常一樣,讓我們??從失敗的測試開始:
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; + } + + }請注意,我馬上就開始使用Java的Function進(jìn)行功能課程。
我偽造實現(xiàn)以使測試通過:
public class FizzBuzzer implements Function<Integer, String> {@Overridepublic String apply(Integer n) { – return null; + return “1”;}}并重構(gòu)測試以消除重復(fù):
public class WhenFunctionallyFuzzingAndBuzzing {@Testpublic 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));}}然后,我添加另一個測試以概括實現(xiàn):
public class WhenFunctionallyFuzzingAndBuzzing {@Testpublic void shouldReplaceMultiplesOfThreeWithFizzAndMultiplesOfFiveWithBuzz() {assertFizzBuzz(“1”, 1); + assertFizzBuzz(“2”, 2);}private void assertFizzBuzz(String expected, int value) {public class FizzBuzzer implements Function<Integer, String> {@Overridepublic String apply(Integer n) { – return “1”; + return Integer.toString(n);}}好的,到目前為止,非常標(biāo)準(zhǔn)的東西。 接下來,我需要將3替換為“ 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> {@Overridepublic 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);}}在這里,我認(rèn)識到我需要根據(jù)輸入應(yīng)用兩個函數(shù)之一。 該代碼有效,但需要進(jìn)行一些清理。 首先,作為墊腳石,我將lambda提取到各個字段中:
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”; +@Overridepublic 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;}}接下來,我強(qiáng)??調(diào)通過提取一個類將“ 3”和“ Fizz”結(jié)合在一起:
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();@Overridepublic 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”; + } + + }在這里,我使用標(biāo)準(zhǔn)的Java Predicate功能接口。
要添加“ Buzz”,我需要將代碼從單個if (隱藏為三元運算符)推廣到循環(huán):
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”) + );@Overridepublic 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; + } + + }糟糕,舊習(xí)慣……那應(yīng)該是一個循環(huán)而不是循環(huán):
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);}}好多了。 下一個測試是用于倍數(shù)的:
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; – } – – }最后一個測試是結(jié)合Fizz和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> {@Overridepublic 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));}}我概括了單一Function到一個Stream的Function S,而我應(yīng)用的map-reduce模式。 我本可以使用.reduce("", (a, b) -> a + b)類的東西來拼寫Reduce的部分,但我認(rèn)為Collectors.joining()更具表現(xiàn)力。
這還沒有通過測試,因為我返回了單個函數(shù)的流。 該修復(fù)有些棘手,因為我需要知道是否找到了任何適用的替換器函數(shù),并且您必須在不終止流的情況下才能做到這一點 。 所以我需要使用StreamSupport創(chuàng)建一個新的流:
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);}}就是這樣。 完整代碼在GitHub上 。
我從這個小練習(xí)中學(xué)到了兩個教訓(xùn):
翻譯自: https://www.javacodegeeks.com/2016/08/functional-fizzbuzz-kata-java.html
總結(jié)
以上是生活随笔為你收集整理的Java中的功能性FizzBuzz Kata的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JavaFX实际应用程序:SkedPal
- 下一篇: 甲骨文第一财季收入约125亿美元增长9%