在柔道练习仅几周之后,我的儿子感到无聊。 他抱怨说自己没有学任何东西,因为他一遍又一遍地做着同样的事情。
混淆学习和做新事物的不仅仅是幼儿。 例如,有多少软件开发人员通过执行kata或参加dojos来进行刻意练习的麻烦?
重复您已经做过很多次的练习似乎很愚蠢,但事实并非如此。 这是在您的领域成为黑带的唯一方法。 并且请记住,精通是三个内在动机之一 (其他是自主性和目的性)。
练习意味着放慢速度,将重点从结果转移到流程。 最好使用可以在有限的时间内完成的简单练习,以便可以多次执行相同的练习。
我发现我在练习时几乎总是学到新东西。 这不是因为我从上次就忘记了解决问题的方法,而是因为自那时以来我已经学到了新事物,因此可以通过新的眼光看到世界。
例如,自Java 8发布以来,我一直在尝试使用新的流类来帮助转变为更具功能性的编程风格。 这改变了我看待旧问题(例如FizzBuzz)的方式 。
让我们看看实际情况。 当然,我首先添加一个测试:
+ package remonsinnema.blog.fizzbuzz;
+
+ import static org.junit.Assert.assertEquals;
+
+ import org.junit.Test;
+
+
+ public class WhenFizzingAndBuzzing {
+
+ private final FizzBuzz fizzbuzz = new FizzBuzz();
+
+ @Test
+ public void shouldReplaceWithFizzAndBuzz() {
+ assertEquals(“1”, “1”, fizzbuzz.get(1));
+ }
+
+ }
该测试使用单元测试的“ 何时……应该”形式, 该形式有助于关注行为而不是实现细节。 我让Eclipse生成进行此编译所需的代码:
+ package remonsinnema.blog.fizzbuzz;
+
+
+ public class FizzBuzz {
+
+ public String get(int i) {
+ return null;
+ }
+
+ }
使测试通过的最简单的代码是伪造它 :
package remonsinnema.blog.fizzbuzz;public class FizzBuzz {public String get(int i) {
– return null;
+ return “1”;}}
现在测试通过了,该重构了 。 我从测试中删除了重复项:
public class WhenFizzingAndBuzzing {@Testpublic void shouldReplaceWithFizzAndBuzz() {
– assertEquals(“1”, “1”, fizzbuzz.get(1));
+ assertFizzBuzz(“1”, 1);
+ }
+
+ private void assertFizzBuzz(String expected, int n) {
+ assertEquals(Integer.toString(n), expected, fizzbuzz.get(n));}}
接下来,我添加一个测试以强制执行真正的实现:
public class WhenFizzingAndBuzzing {@Testpublic void shouldReplaceWithFizzAndBuzz() {assertFizzBuzz(“1”, 1);
+ assertFizzBuzz(“2”, 2);}private void assertFizzBuzz(String expected, int n) {package remonsinnema.blog.fizzbuzz;public class FizzBuzz {
– public String get(int i) {
– return “1”;
+ public String get(int n) {
+ return Integer.toString(n);}}
好的,现在让我们对Fizz进行测试:
public class WhenFizzingAndBuzzing {
public void shouldReplaceWithFizzAndBuzz() {
assertFizzBuzz(“1”, 1);
assertFizzBuzz(“2”, 2);
+ assertFizzBuzz(“Fizz”, 3);
}
private void assertFizzBuzz(String expected, int n) {
package remonsinnema.blog.fizzbuzz;
public class FizzBuzz {
public String get(int n) {
+ if (n == 3) {
+ return “Fizz”;
+ }
return Integer.toString(n);
}
与Buzz类似:
public class WhenFizzingAndBuzzing {assertFizzBuzz(“Fizz”, 3);
+ assertFizzBuzz(“4”, 4);
+ assertFizzBuzz(“Buzz”, 5);}private void assertFizzBuzz(String expected, int n) {public class FizzBuzz {if (n == 3) {return “Fizz”;}
+ if (n == 5) {
+ return “Buzz”;
+ }return Integer.toString(n);}
在这里,我只是复制并粘贴了if
语句以使其快速运行 。 当然,我们不应该在这里停下来,而要摆脱肮脏的东西。 在这种情况下,那是重复的。
首先,让我们更新代码以使重复更加明显:
package remonsinnema.blog.fizzbuzz;public class FizzBuzz {public String get(int n) {
– if (n == 3) {
– return “Fizz”;
+ MultipleReplacer replacer = new MultipleReplacer(3, “Fizz”);
+ if (n == replacer.getValue()) {
+ return replacer.getText();}
– if (n == 5) {
– return “Buzz”;
+ replacer = new MultipleReplacer(5, “Buzz”);
+ if (n == replacer.getValue()) {
+ return replacer.getText();}return Integer.toString(n);}
+ package remonsinnema.blog.fizzbuzz;
+
+
+ public class MultipleReplacer {
+
+ private final int value;
+ private final String text;
+
+ public MultipleReplacer(int value, String text) {
+ this.value = value;
+ this.text = text;
+ }
+
+ public int getValue() {
+ return value;
+ }
+
+ public String getText() {
+ return text;
+ }
+
+ }
我刚刚创建了一个新的值对象,以保存在复制/粘贴后必须更改的两个值。
现在,重复项更加清晰了,可以轻松删除它:
package remonsinnema.blog.fizzbuzz;
+ import java.util.Arrays;
+ import java.util.Collection;
+public class FizzBuzz {
+ private final Collection replacers = Arrays.asList(
+ new MultipleReplacer(3, “Fizz”), new MultipleReplacer(5, “Buzz”));
+public String get(int n) {
– MultipleReplacer replacer = new MultipleReplacer(3, “Fizz”);
– if (n == replacer.getValue()) {
– return replacer.getText();
– }
– replacer = new MultipleReplacer(5, “Buzz”);
– if (n == replacer.getValue()) {
– return replacer.getText();
+ for (MultipleReplacer replacer : replacers) {
+ if (n == replacer.getValue()) {
+ return replacer.getText();
+ }}return Integer.toString(n);}
但是,我还没有完成清理工作。 当前代码受功能嫉妒之苦 ,我可以通过将行为移入值对象来解决:
package remonsinnema.blog.fizzbuzz;import java.util.Arrays;import java.util.Collection;
+ import java.util.Optional;public class FizzBuzz {public String get(int n) {for (MultipleReplacer replacer : replacers) {
– if (n == replacer.getValue()) {
– return replacer.getText();
+ Optional result = replacer.textFor(n);
+ if (result.isPresent()) {
+ return result.get();}}return Integer.toString(n);
package remonsinnema.blog.fizzbuzz;
+ import java.util.Optional;
+public class MultipleReplacer {this.text = text;}
– public int getValue() {
– return value;
– }
–
– public String getText() {
– return text;
+ public Optional<String> textFor(int n) {
+ if (n == value) {
+ return Optional.of(text);
+ }
+ return Optional.empty();}}
现在,我已经完成了重构,我可以继续使用倍数:
public class WhenFizzingAndBuzzing {assertFizzBuzz(“Fizz”, 3);assertFizzBuzz(“4”, 4);assertFizzBuzz(“Buzz”, 5);
+ assertFizzBuzz(“Fizz”, 6);}private void assertFizzBuzz(String expected, int n) {public class MultipleReplacer {}public Optional<String> textFor(int n) {
– if (n == value) {
+ if (n % value == 0) {return Optional.of(text);}return Optional.empty();
最终测试是同时进行“嘶嘶声”和“嗡嗡声”测试:
public class WhenFizzingAndBuzzing {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 n) {
public class FizzBuzz {public class FizzBuzz {new MultipleReplacer(3, “Fizz”), new MultipleReplacer(5, “Buzz”));public String get(int n) {
+ StringBuilder result = new StringBuilder();for (MultipleReplacer replacer : replacers) {
– Optional<String> result = replacer.textFor(n);
– if (result.isPresent()) {
– return result.get();
+ Optional<String> replacement = replacer.textFor(n);
+ if (replacement.isPresent()) {
+ result.append(replacement.get());}}
+ if (result.length() > 0) {
+ return result.toString();
+ }return Integer.toString(n);}}
这段代码很复杂,但这是流可以解决的地方:
public class FizzBuzz {new MultipleReplacer(3, “Fizz”), new MultipleReplacer(5, “Buzz”));public String get(int n) {
– StringBuilder result = new StringBuilder();
– for (MultipleReplacer replacer : replacers) {
– Optional<String> replacement = replacer.textFor(n);
– if (replacement.isPresent()) {
– result.append(replacement.get());
– }
– }
– if (result.length() > 0) {
– return result.toString();
– }
– return Integer.toString(n);
+ return replacers.stream()
+ .map(replacer -> replacer.textFor(n))
+ .filter(Optional::isPresent)
+ .map(optional -> optional.get())
+ .reduce((a, b) -> a + b)
+ .orElse(Integer.toString(n));}}
请注意for
和if
语句如何消失。 而不是拼写出的东西需要如何做,我们说什么 ,我们要实现的。
我们可以应用相同的技巧来除去ode基数中剩余的if
语句:
public class MultipleReplacer {}public Optional<String> textFor(int n) {
– if (n % value == 0) {
– return Optional.of(text);
– }
– return Optional.empty();
+ return Optional.of(text)
+ .filter(ignored -> n % value == 0);}}
该代码在GitHub上 。
翻译自: https://www.javacodegeeks.com/2016/05/fizzbuzz-kata-java-streams.html