После пары недель занятий дзюдо моему сыну стало скучно. Он жаловался, что ничего не учил, потому что продолжал делать одно и то же снова и снова.
Это не просто маленькие дети, которые путают обучение и делать новые вещи. Например, сколько разработчиков программного обеспечения сталкиваются с проблемой преднамеренной практики , выполняя каты или посещая додзё ?
Может показаться глупым повторять упражнения, которые вы уже делали много раз, но это не так. Это единственный способ стать черным поясом в своей области. И помните, что мастерство является одним из трех внутренних мотиваторов (другие — это автономия и цель).
Практика означает замедление и перемещение внимания от результата к процессу. Лучше всего использовать простые упражнения, которые вы можете выполнить за ограниченное время, чтобы вы могли выполнять одно и то же упражнение несколько раз.
Я обнаружил, что практически всегда узнаю что-то новое, когда практикуюсь. Это не потому, что я забыл, как решить проблему с прошлого раза, а потому, что с тех пор я узнал что-то новое и, таким образом, вижу мир новыми глазами.
Например, после выхода Java 8 я пытался использовать новые потоковые классы, чтобы помочь перейти к более функциональному стилю программирования. Это изменило мой взгляд на старые проблемы, такие как FizzBuzz .
Давайте посмотрим это в действии. Конечно, я начинаю с добавления теста:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
+ 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 сгенерировать код, необходимый для компиляции:
01
02
03
04
05
06
07
08
09
10
|
+ package remonsinnema.blog.fizzbuzz; + + + public class FizzBuzz { + + public String get( int i) { + return null ; + } + + } |
Простейший код, который делает тестовый проход, состоит в том, чтобы подделать его :
1
2
3
4
5
6
7
|
package remonsinnema.blog.fizzbuzz; public class FizzBuzz { public String get( int i) { – return null ; + return “ 1 ”; } } |
Теперь, когда тест пройден, пришло время рефакторинга . Я удаляю дублирование из теста:
01
02
03
04
05
06
07
08
09
10
11
|
public class WhenFizzingAndBuzzing { @Test public 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)); } } |
Далее я добавляю тест, чтобы вызвать реальную реализацию:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
public class WhenFizzingAndBuzzing { @Test public 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 :
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
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 :
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
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
чтобы он работал быстро . Мы не должны останавливаться на достигнутом, конечно, но избавиться от грязных вещей. В данном случае это дублирование.
Во-первых, давайте обновим код, чтобы сделать дублирование более очевидным:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
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); } |
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
|
+ 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; + } + + } |
Я только что создал новый объект значения для хранения двух значений, которые мне пришлось изменить после копирования / вставки.
Теперь, когда дублирование стало понятнее, его легко удалить:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
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); } |
Я не закончил уборку, однако. Текущий код страдает от зависти к функциям , которую я решаю, перемещая поведение в объект значения:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
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); |
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
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(); } } |
Теперь, когда я закончил рефакторинг, я могу продолжить с кратных значений:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
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(); |
Финальный тест для одновременного «Fizz» и «Buzz»:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
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) { |
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
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); } } |
Этот код довольно сложный, но здесь на помощь приходят потоки:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
|
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
. Вместо того, чтобы объяснять, как что-то нужно сделать, мы говорим, чего хотим достичь.
Мы можем применить тот же трюк, чтобы избавиться от оставшегося оператора if
в нашей базе од:
01
02
03
04
05
06
07
08
09
10
11
|
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 .
Ссылка: | FizzBuzz Kata с потоками Java от нашего партнера по JCG Ремона Синнема в блоге по разработке безопасного программного обеспечения . |