From 8c6aa5bd5a0c966dc8e79b27ca1e7e65ceea6c1d Mon Sep 17 00:00:00 2001 From: "jake.b" Date: Tue, 16 May 2023 15:17:25 +0900 Subject: [PATCH 1/6] refactor: use streams on optionals --- src/main/java/nextstep/fp/Filter.java | 5 +++ src/main/java/nextstep/fp/Lambda.java | 29 ++--------------- src/main/java/nextstep/fp/StreamStudy.java | 32 +++++++++++++------ .../java/nextstep/optional/ComputerStore.java | 5 ++- .../java/nextstep/optional/Expression.java | 13 ++++---- src/main/java/nextstep/optional/User.java | 7 +++- src/main/java/nextstep/optional/Users.java | 10 +++--- src/test/java/nextstep/fp/CarTest.java | 14 ++------ src/test/java/nextstep/fp/LambdaTest.java | 6 ++-- 9 files changed, 55 insertions(+), 66 deletions(-) create mode 100644 src/main/java/nextstep/fp/Filter.java diff --git a/src/main/java/nextstep/fp/Filter.java b/src/main/java/nextstep/fp/Filter.java new file mode 100644 index 0000000000..3dbd5097ee --- /dev/null +++ b/src/main/java/nextstep/fp/Filter.java @@ -0,0 +1,5 @@ +package nextstep.fp; + +public interface Filter { + boolean isPassed (Integer number); +} diff --git a/src/main/java/nextstep/fp/Lambda.java b/src/main/java/nextstep/fp/Lambda.java index bd68fe1ce6..7d63856c7f 100644 --- a/src/main/java/nextstep/fp/Lambda.java +++ b/src/main/java/nextstep/fp/Lambda.java @@ -18,36 +18,13 @@ public static void printAllLambda(List numbers) { } public static void runThread() { - new Thread(new Runnable() { - @Override - public void run() { - System.out.println("Hello from thread"); - } - }).start(); - } - - public static int sumAll(List numbers) { - int total = 0; - for (int number : numbers) { - total += number; - } - return total; - } - - public static int sumAllEven(List numbers) { - int total = 0; - for (int number : numbers) { - if (number % 2 == 0) { - total += number; - } - } - return total; + new Thread(() -> System.out.println("Hello from thread")).start(); } - public static int sumAllOverThree(List numbers) { + public static int sum(List numbers, Filter filter) { int total = 0; for (int number : numbers) { - if (number > 3) { + if (filter.isPassed(number)){ total += number; } } diff --git a/src/main/java/nextstep/fp/StreamStudy.java b/src/main/java/nextstep/fp/StreamStudy.java index b446983a02..46e1c9bb7d 100644 --- a/src/main/java/nextstep/fp/StreamStudy.java +++ b/src/main/java/nextstep/fp/StreamStudy.java @@ -5,15 +5,16 @@ import java.nio.file.Files; import java.nio.file.Paths; import java.util.Arrays; +import java.util.Comparator; import java.util.List; import java.util.stream.Collectors; public class StreamStudy { public static long countWords() throws IOException { - String contents = new String(Files.readAllBytes(Paths - .get("src/main/resources/fp/war-and-peace.txt")), StandardCharsets.UTF_8); - List words = Arrays.asList(contents.split("[\\P{L}]+")); + String contents = Files.readString(Paths + .get("src/main/resources/fp/war-and-peace.txt")); + String[] words = contents.split("\\P{L}"); long count = 0; for (String w : words) { @@ -23,11 +24,17 @@ public static long countWords() throws IOException { } public static void printLongestWordTop100() throws IOException { - String contents = new String(Files.readAllBytes(Paths - .get("src/main/resources/fp/war-and-peace.txt")), StandardCharsets.UTF_8); - List words = Arrays.asList(contents.split("[\\P{L}]+")); - - // TODO 이 부분에 구현한다. + String contents = Files.readString(Paths + .get("src/main/resources/fp/war-and-peace.txt")); + List words = Arrays.asList(contents.split("\\P{L}")); + + words.stream() + .filter(word -> word.length() > 12) + .sorted(Comparator.comparingInt(String::length)) + .distinct() + .limit(100) + .map(String::toLowerCase) + .forEach(System.out::println); } public static List doubleNumbers(List numbers) { @@ -35,10 +42,15 @@ public static List doubleNumbers(List numbers) { } public static long sumAll(List numbers) { - return numbers.stream().reduce(0, (x, y) -> x + y); + return numbers.stream().reduce(0, Integer::sum); } public static long sumOverThreeAndDouble(List numbers) { - return 0; + return numbers.stream() + .filter(number -> number > 3) + .map(number -> number * 2) + .mapToLong(number -> number) + .reduce(Long::sum) + .orElse(0); } } \ No newline at end of file diff --git a/src/main/java/nextstep/optional/ComputerStore.java b/src/main/java/nextstep/optional/ComputerStore.java index 2695c967ac..048065734f 100644 --- a/src/main/java/nextstep/optional/ComputerStore.java +++ b/src/main/java/nextstep/optional/ComputerStore.java @@ -3,6 +3,8 @@ import nextstep.optional.Computer.Soundcard; import nextstep.optional.Computer.USB; +import java.util.Optional; + public class ComputerStore { public static final String UNKNOWN_VERSION = "UNKNOWN"; @@ -21,6 +23,7 @@ public static String getVersion(Computer computer) { } public static String getVersionOptional(Computer computer) { - return null; + return Optional.ofNullable(getVersion(computer)) + .orElse(UNKNOWN_VERSION); } } diff --git a/src/main/java/nextstep/optional/Expression.java b/src/main/java/nextstep/optional/Expression.java index 1c98cd6a62..9937f2711e 100644 --- a/src/main/java/nextstep/optional/Expression.java +++ b/src/main/java/nextstep/optional/Expression.java @@ -1,5 +1,7 @@ package nextstep.optional; +import java.util.Arrays; + enum Expression { PLUS("+"), MINUS("-"), TIMES("*"), DIVIDE("/"); @@ -14,12 +16,9 @@ private static boolean matchExpression(Expression e, String expression) { } static Expression of(String expression) { - for (Expression v : values()) { - if (matchExpression(v, expression)) { - return v; - } - } - - throw new IllegalArgumentException(String.format("%s는 사칙연산에 해당하지 않는 표현식입니다.", expression)); + return Arrays.stream(values()) + .filter(v -> matchExpression(v, expression)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException(String.format("%s는 사칙연산에 해당하지 않는 표현식입니다.", expression))); } } diff --git a/src/main/java/nextstep/optional/User.java b/src/main/java/nextstep/optional/User.java index 9614c2f43f..bc2aee7eb9 100644 --- a/src/main/java/nextstep/optional/User.java +++ b/src/main/java/nextstep/optional/User.java @@ -1,5 +1,7 @@ package nextstep.optional; +import java.util.Optional; + public class User { private String name; private Integer age; @@ -33,7 +35,10 @@ public static boolean ageIsInRange1(User user) { } public static boolean ageIsInRange2(User user) { - return false; + return Optional.ofNullable(user) + .map(User::getAge) + .filter(age -> age >= 30 && age <= 45) + .isPresent(); } @Override diff --git a/src/main/java/nextstep/optional/Users.java b/src/main/java/nextstep/optional/Users.java index 6293040de8..16b0c1c317 100644 --- a/src/main/java/nextstep/optional/Users.java +++ b/src/main/java/nextstep/optional/Users.java @@ -13,11 +13,9 @@ public class Users { new User("honux", 45)); User getUser(String name) { - for (User user : users) { - if (user.matchName(name)) { - return user; - } - } - return DEFAULT_USER; + return users.stream() + .filter(user -> user.matchName(name)) + .findAny() + .orElse(DEFAULT_USER); } } diff --git a/src/test/java/nextstep/fp/CarTest.java b/src/test/java/nextstep/fp/CarTest.java index 1ab1106fe2..ecab481aec 100644 --- a/src/test/java/nextstep/fp/CarTest.java +++ b/src/test/java/nextstep/fp/CarTest.java @@ -8,24 +8,14 @@ public class CarTest { @Test public void 이동() { Car car = new Car("pobi", 0); - Car actual = car.move(new MoveStrategy() { - @Override - public boolean isMovable() { - return true; - } - }); + Car actual = car.move(() -> true); assertThat(actual).isEqualTo(new Car("pobi", 1)); } @Test public void 정지() { Car car = new Car("pobi", 0); - Car actual = car.move(new MoveStrategy() { - @Override - public boolean isMovable() { - return false; - } - }); + Car actual = car.move(() -> false); assertThat(actual).isEqualTo(new Car("pobi", 0)); } } diff --git a/src/test/java/nextstep/fp/LambdaTest.java b/src/test/java/nextstep/fp/LambdaTest.java index f240ac6560..1564cfab76 100644 --- a/src/test/java/nextstep/fp/LambdaTest.java +++ b/src/test/java/nextstep/fp/LambdaTest.java @@ -33,19 +33,19 @@ public void runThread() throws Exception { @Test public void sumAll() throws Exception { - int sum = Lambda.sumAll(numbers); + int sum = Lambda.sum(numbers, number -> true); assertThat(sum).isEqualTo(21); } @Test public void sumAllEven() throws Exception { - int sum = Lambda.sumAllEven(numbers); + int sum = Lambda.sum(numbers, number -> number % 2 == 0); assertThat(sum).isEqualTo(12); } @Test public void sumAllOverThree() throws Exception { - int sum = Lambda.sumAllOverThree(numbers); + int sum = Lambda.sum(numbers, number -> number > 3); assertThat(sum).isEqualTo(15); } } From 1f5acb8eaebbf0b414e1508a676044cda449d78d Mon Sep 17 00:00:00 2001 From: "jake.b" Date: Tue, 16 May 2023 15:17:25 +0900 Subject: [PATCH 2/6] refactor: use streams and optionals --- src/main/java/nextstep/fp/Filter.java | 5 +++ src/main/java/nextstep/fp/Lambda.java | 29 ++--------------- src/main/java/nextstep/fp/StreamStudy.java | 32 +++++++++++++------ .../java/nextstep/optional/ComputerStore.java | 5 ++- .../java/nextstep/optional/Expression.java | 13 ++++---- src/main/java/nextstep/optional/User.java | 7 +++- src/main/java/nextstep/optional/Users.java | 10 +++--- src/test/java/nextstep/fp/CarTest.java | 14 ++------ src/test/java/nextstep/fp/LambdaTest.java | 6 ++-- 9 files changed, 55 insertions(+), 66 deletions(-) create mode 100644 src/main/java/nextstep/fp/Filter.java diff --git a/src/main/java/nextstep/fp/Filter.java b/src/main/java/nextstep/fp/Filter.java new file mode 100644 index 0000000000..3dbd5097ee --- /dev/null +++ b/src/main/java/nextstep/fp/Filter.java @@ -0,0 +1,5 @@ +package nextstep.fp; + +public interface Filter { + boolean isPassed (Integer number); +} diff --git a/src/main/java/nextstep/fp/Lambda.java b/src/main/java/nextstep/fp/Lambda.java index bd68fe1ce6..7d63856c7f 100644 --- a/src/main/java/nextstep/fp/Lambda.java +++ b/src/main/java/nextstep/fp/Lambda.java @@ -18,36 +18,13 @@ public static void printAllLambda(List numbers) { } public static void runThread() { - new Thread(new Runnable() { - @Override - public void run() { - System.out.println("Hello from thread"); - } - }).start(); - } - - public static int sumAll(List numbers) { - int total = 0; - for (int number : numbers) { - total += number; - } - return total; - } - - public static int sumAllEven(List numbers) { - int total = 0; - for (int number : numbers) { - if (number % 2 == 0) { - total += number; - } - } - return total; + new Thread(() -> System.out.println("Hello from thread")).start(); } - public static int sumAllOverThree(List numbers) { + public static int sum(List numbers, Filter filter) { int total = 0; for (int number : numbers) { - if (number > 3) { + if (filter.isPassed(number)){ total += number; } } diff --git a/src/main/java/nextstep/fp/StreamStudy.java b/src/main/java/nextstep/fp/StreamStudy.java index b446983a02..46e1c9bb7d 100644 --- a/src/main/java/nextstep/fp/StreamStudy.java +++ b/src/main/java/nextstep/fp/StreamStudy.java @@ -5,15 +5,16 @@ import java.nio.file.Files; import java.nio.file.Paths; import java.util.Arrays; +import java.util.Comparator; import java.util.List; import java.util.stream.Collectors; public class StreamStudy { public static long countWords() throws IOException { - String contents = new String(Files.readAllBytes(Paths - .get("src/main/resources/fp/war-and-peace.txt")), StandardCharsets.UTF_8); - List words = Arrays.asList(contents.split("[\\P{L}]+")); + String contents = Files.readString(Paths + .get("src/main/resources/fp/war-and-peace.txt")); + String[] words = contents.split("\\P{L}"); long count = 0; for (String w : words) { @@ -23,11 +24,17 @@ public static long countWords() throws IOException { } public static void printLongestWordTop100() throws IOException { - String contents = new String(Files.readAllBytes(Paths - .get("src/main/resources/fp/war-and-peace.txt")), StandardCharsets.UTF_8); - List words = Arrays.asList(contents.split("[\\P{L}]+")); - - // TODO 이 부분에 구현한다. + String contents = Files.readString(Paths + .get("src/main/resources/fp/war-and-peace.txt")); + List words = Arrays.asList(contents.split("\\P{L}")); + + words.stream() + .filter(word -> word.length() > 12) + .sorted(Comparator.comparingInt(String::length)) + .distinct() + .limit(100) + .map(String::toLowerCase) + .forEach(System.out::println); } public static List doubleNumbers(List numbers) { @@ -35,10 +42,15 @@ public static List doubleNumbers(List numbers) { } public static long sumAll(List numbers) { - return numbers.stream().reduce(0, (x, y) -> x + y); + return numbers.stream().reduce(0, Integer::sum); } public static long sumOverThreeAndDouble(List numbers) { - return 0; + return numbers.stream() + .filter(number -> number > 3) + .map(number -> number * 2) + .mapToLong(number -> number) + .reduce(Long::sum) + .orElse(0); } } \ No newline at end of file diff --git a/src/main/java/nextstep/optional/ComputerStore.java b/src/main/java/nextstep/optional/ComputerStore.java index 2695c967ac..048065734f 100644 --- a/src/main/java/nextstep/optional/ComputerStore.java +++ b/src/main/java/nextstep/optional/ComputerStore.java @@ -3,6 +3,8 @@ import nextstep.optional.Computer.Soundcard; import nextstep.optional.Computer.USB; +import java.util.Optional; + public class ComputerStore { public static final String UNKNOWN_VERSION = "UNKNOWN"; @@ -21,6 +23,7 @@ public static String getVersion(Computer computer) { } public static String getVersionOptional(Computer computer) { - return null; + return Optional.ofNullable(getVersion(computer)) + .orElse(UNKNOWN_VERSION); } } diff --git a/src/main/java/nextstep/optional/Expression.java b/src/main/java/nextstep/optional/Expression.java index 1c98cd6a62..9937f2711e 100644 --- a/src/main/java/nextstep/optional/Expression.java +++ b/src/main/java/nextstep/optional/Expression.java @@ -1,5 +1,7 @@ package nextstep.optional; +import java.util.Arrays; + enum Expression { PLUS("+"), MINUS("-"), TIMES("*"), DIVIDE("/"); @@ -14,12 +16,9 @@ private static boolean matchExpression(Expression e, String expression) { } static Expression of(String expression) { - for (Expression v : values()) { - if (matchExpression(v, expression)) { - return v; - } - } - - throw new IllegalArgumentException(String.format("%s는 사칙연산에 해당하지 않는 표현식입니다.", expression)); + return Arrays.stream(values()) + .filter(v -> matchExpression(v, expression)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException(String.format("%s는 사칙연산에 해당하지 않는 표현식입니다.", expression))); } } diff --git a/src/main/java/nextstep/optional/User.java b/src/main/java/nextstep/optional/User.java index 9614c2f43f..bc2aee7eb9 100644 --- a/src/main/java/nextstep/optional/User.java +++ b/src/main/java/nextstep/optional/User.java @@ -1,5 +1,7 @@ package nextstep.optional; +import java.util.Optional; + public class User { private String name; private Integer age; @@ -33,7 +35,10 @@ public static boolean ageIsInRange1(User user) { } public static boolean ageIsInRange2(User user) { - return false; + return Optional.ofNullable(user) + .map(User::getAge) + .filter(age -> age >= 30 && age <= 45) + .isPresent(); } @Override diff --git a/src/main/java/nextstep/optional/Users.java b/src/main/java/nextstep/optional/Users.java index 6293040de8..16b0c1c317 100644 --- a/src/main/java/nextstep/optional/Users.java +++ b/src/main/java/nextstep/optional/Users.java @@ -13,11 +13,9 @@ public class Users { new User("honux", 45)); User getUser(String name) { - for (User user : users) { - if (user.matchName(name)) { - return user; - } - } - return DEFAULT_USER; + return users.stream() + .filter(user -> user.matchName(name)) + .findAny() + .orElse(DEFAULT_USER); } } diff --git a/src/test/java/nextstep/fp/CarTest.java b/src/test/java/nextstep/fp/CarTest.java index 1ab1106fe2..ecab481aec 100644 --- a/src/test/java/nextstep/fp/CarTest.java +++ b/src/test/java/nextstep/fp/CarTest.java @@ -8,24 +8,14 @@ public class CarTest { @Test public void 이동() { Car car = new Car("pobi", 0); - Car actual = car.move(new MoveStrategy() { - @Override - public boolean isMovable() { - return true; - } - }); + Car actual = car.move(() -> true); assertThat(actual).isEqualTo(new Car("pobi", 1)); } @Test public void 정지() { Car car = new Car("pobi", 0); - Car actual = car.move(new MoveStrategy() { - @Override - public boolean isMovable() { - return false; - } - }); + Car actual = car.move(() -> false); assertThat(actual).isEqualTo(new Car("pobi", 0)); } } diff --git a/src/test/java/nextstep/fp/LambdaTest.java b/src/test/java/nextstep/fp/LambdaTest.java index f240ac6560..1564cfab76 100644 --- a/src/test/java/nextstep/fp/LambdaTest.java +++ b/src/test/java/nextstep/fp/LambdaTest.java @@ -33,19 +33,19 @@ public void runThread() throws Exception { @Test public void sumAll() throws Exception { - int sum = Lambda.sumAll(numbers); + int sum = Lambda.sum(numbers, number -> true); assertThat(sum).isEqualTo(21); } @Test public void sumAllEven() throws Exception { - int sum = Lambda.sumAllEven(numbers); + int sum = Lambda.sum(numbers, number -> number % 2 == 0); assertThat(sum).isEqualTo(12); } @Test public void sumAllOverThree() throws Exception { - int sum = Lambda.sumAllOverThree(numbers); + int sum = Lambda.sum(numbers, number -> number > 3); assertThat(sum).isEqualTo(15); } } From fae458df9879e8d536079976322818003a3f8462 Mon Sep 17 00:00:00 2001 From: "jake.b" Date: Thu, 25 May 2023 14:49:06 +0900 Subject: [PATCH 3/6] feature: set users and levels --- src/main/java/nextstep/ladder/Ladder.java | 11 ++++++++++ src/main/java/nextstep/ladder/Leg.java | 4 ++++ src/main/java/nextstep/ladder/Legs.java | 4 ++++ src/main/java/nextstep/ladder/Position.java | 4 ++++ src/main/java/nextstep/ladder/Users.java | 17 ++++++++++++++++ src/test/java/nextstep/ladder/LadderTest.java | 20 +++++++++++++++++++ 6 files changed, 60 insertions(+) create mode 100644 src/main/java/nextstep/ladder/Ladder.java create mode 100644 src/main/java/nextstep/ladder/Leg.java create mode 100644 src/main/java/nextstep/ladder/Legs.java create mode 100644 src/main/java/nextstep/ladder/Position.java create mode 100644 src/main/java/nextstep/ladder/Users.java create mode 100644 src/test/java/nextstep/ladder/LadderTest.java diff --git a/src/main/java/nextstep/ladder/Ladder.java b/src/main/java/nextstep/ladder/Ladder.java new file mode 100644 index 0000000000..c6210758ea --- /dev/null +++ b/src/main/java/nextstep/ladder/Ladder.java @@ -0,0 +1,11 @@ +package nextstep.ladder; + +public class Ladder { + public final int levels; + public final Users users; + + public Ladder(Users users, int levels) { + this.levels = levels; + this.users = users; + } +} diff --git a/src/main/java/nextstep/ladder/Leg.java b/src/main/java/nextstep/ladder/Leg.java new file mode 100644 index 0000000000..1c0fe4a8e4 --- /dev/null +++ b/src/main/java/nextstep/ladder/Leg.java @@ -0,0 +1,4 @@ +package nextstep.ladder; + +public class Leg { +} diff --git a/src/main/java/nextstep/ladder/Legs.java b/src/main/java/nextstep/ladder/Legs.java new file mode 100644 index 0000000000..7bedecafb3 --- /dev/null +++ b/src/main/java/nextstep/ladder/Legs.java @@ -0,0 +1,4 @@ +package nextstep.ladder; + +public class Legs { +} diff --git a/src/main/java/nextstep/ladder/Position.java b/src/main/java/nextstep/ladder/Position.java new file mode 100644 index 0000000000..4876e06a80 --- /dev/null +++ b/src/main/java/nextstep/ladder/Position.java @@ -0,0 +1,4 @@ +package nextstep.ladder; + +public class Position { +} diff --git a/src/main/java/nextstep/ladder/Users.java b/src/main/java/nextstep/ladder/Users.java new file mode 100644 index 0000000000..d0a85eca74 --- /dev/null +++ b/src/main/java/nextstep/ladder/Users.java @@ -0,0 +1,17 @@ +package nextstep.ladder; + +import java.util.Iterator; +import java.util.List; + +public class Users implements Iterable { + private final List users; + + public Users(List users) { + this.users = users; + } + + @Override + public Iterator iterator() { + return users.iterator(); + } +} diff --git a/src/test/java/nextstep/ladder/LadderTest.java b/src/test/java/nextstep/ladder/LadderTest.java new file mode 100644 index 0000000000..367009829c --- /dev/null +++ b/src/test/java/nextstep/ladder/LadderTest.java @@ -0,0 +1,20 @@ +package nextstep.ladder; + +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; + +public class LadderTest { + @Test + public void levelAndUsersAreSet() { + var levels = 5; + var users = new Users(List.of("erik", "jake", "david")); + + var ladder = new Ladder(users, levels); + + assertThat(ladder.levels).isEqualTo(levels); + assertThat(ladder.users).containsExactly("erik", "jake", "david"); + } +} From 2aa5b581b39860ae025e35e53a2186fc9f8c9f1f Mon Sep 17 00:00:00 2001 From: "jake.b" Date: Thu, 25 May 2023 16:25:41 +0900 Subject: [PATCH 4/6] feature: ladder leg placement --- .../ladder/AlwaysGenerateStrategy.java | 9 +++++ .../nextstep/ladder/GenerationStrategy.java | 5 +++ src/main/java/nextstep/ladder/Ladder.java | 8 ++-- src/main/java/nextstep/ladder/Leg.java | 4 -- src/main/java/nextstep/ladder/Legs.java | 38 ++++++++++++++++++ src/main/java/nextstep/ladder/Natural.java | 39 ++++++++++++++++++ src/main/java/nextstep/ladder/Position.java | 32 +++++++++++++++ src/main/java/nextstep/ladder/Users.java | 4 ++ src/test/java/nextstep/ladder/LadderTest.java | 40 +++++++++++++++++-- 9 files changed, 169 insertions(+), 10 deletions(-) create mode 100644 src/main/java/nextstep/ladder/AlwaysGenerateStrategy.java create mode 100644 src/main/java/nextstep/ladder/GenerationStrategy.java delete mode 100644 src/main/java/nextstep/ladder/Leg.java create mode 100644 src/main/java/nextstep/ladder/Natural.java diff --git a/src/main/java/nextstep/ladder/AlwaysGenerateStrategy.java b/src/main/java/nextstep/ladder/AlwaysGenerateStrategy.java new file mode 100644 index 0000000000..a5e5b96cda --- /dev/null +++ b/src/main/java/nextstep/ladder/AlwaysGenerateStrategy.java @@ -0,0 +1,9 @@ +package nextstep.ladder; + +public class AlwaysGenerateStrategy implements GenerationStrategy{ + + @Override + public boolean shouldPlace(boolean isPlaceable) { + return isPlaceable; + } +} diff --git a/src/main/java/nextstep/ladder/GenerationStrategy.java b/src/main/java/nextstep/ladder/GenerationStrategy.java new file mode 100644 index 0000000000..e4d974c1e9 --- /dev/null +++ b/src/main/java/nextstep/ladder/GenerationStrategy.java @@ -0,0 +1,5 @@ +package nextstep.ladder; + +public interface GenerationStrategy { + boolean shouldPlace(boolean isPlaceable); +} diff --git a/src/main/java/nextstep/ladder/Ladder.java b/src/main/java/nextstep/ladder/Ladder.java index c6210758ea..4f11ec2873 100644 --- a/src/main/java/nextstep/ladder/Ladder.java +++ b/src/main/java/nextstep/ladder/Ladder.java @@ -1,11 +1,13 @@ package nextstep.ladder; public class Ladder { - public final int levels; + public final Natural height; public final Users users; + public final Legs legs; - public Ladder(Users users, int levels) { - this.levels = levels; + public Ladder(Users users, Natural levels, GenerationStrategy strategy) { + this.height = levels; this.users = users; + legs = new Legs(height, users.size(), strategy); } } diff --git a/src/main/java/nextstep/ladder/Leg.java b/src/main/java/nextstep/ladder/Leg.java deleted file mode 100644 index 1c0fe4a8e4..0000000000 --- a/src/main/java/nextstep/ladder/Leg.java +++ /dev/null @@ -1,4 +0,0 @@ -package nextstep.ladder; - -public class Leg { -} diff --git a/src/main/java/nextstep/ladder/Legs.java b/src/main/java/nextstep/ladder/Legs.java index 7bedecafb3..b7aa28b282 100644 --- a/src/main/java/nextstep/ladder/Legs.java +++ b/src/main/java/nextstep/ladder/Legs.java @@ -1,4 +1,42 @@ package nextstep.ladder; +import java.util.HashSet; +import java.util.Set; +import java.util.stream.LongStream; + public class Legs { + private final Set legs; + + public Legs(Natural height, Natural width, GenerationStrategy strategy) { + legs = new HashSet<>(); + placeLegs(height, width, strategy); + } + + private void placeLegs(Natural height, Natural width, GenerationStrategy strategy) { + LongStream.range(0, height.value()) + .mapToObj(Natural::new) + .forEach(level -> placeLegsInLevel(level, width, strategy)); + } + + private void placeLegsInLevel(Natural level, Natural width, GenerationStrategy strategy) { + LongStream.range(0, width.value()) + .mapToObj(Natural::new) + .map(place -> new Position(level, place)) + .forEach(position -> placeLegWithStrategy(strategy, position)); + } + + private void placeLegWithStrategy(GenerationStrategy strategy, Position position) { + var isPlaceable = isOnTheLeftEdge(position) || !hasLegOnRightSide(position.getLeftPosition()); + if (strategy.shouldPlace(isPlaceable)) { + legs.add(position); + } + } + + private boolean isOnTheLeftEdge(Position position) { + return position.place.value() == 0; + } + + public boolean hasLegOnRightSide(Position position) { + return legs.contains(position); + } } diff --git a/src/main/java/nextstep/ladder/Natural.java b/src/main/java/nextstep/ladder/Natural.java new file mode 100644 index 0000000000..43bc5c7a91 --- /dev/null +++ b/src/main/java/nextstep/ladder/Natural.java @@ -0,0 +1,39 @@ +package nextstep.ladder; + +public class Natural implements Comparable { + private final Long value; + + public Natural(long value) { + if (value < 0) { + throw new IllegalArgumentException("Negative value given for natural number"); + } + this.value = value; + } + + public Long value() { + return value; + } + + @Override + public boolean equals(Object o) { + if (o instanceof Natural) { + return value.equals(((Natural) o).value); + } + return false; + } + + @Override + public int hashCode() { + return value.hashCode(); + } + + @Override + public String toString() { + return value.toString(); + } + + @Override + public int compareTo(Natural o) { + return value.compareTo(o.value); + } +} diff --git a/src/main/java/nextstep/ladder/Position.java b/src/main/java/nextstep/ladder/Position.java index 4876e06a80..9925cebf5a 100644 --- a/src/main/java/nextstep/ladder/Position.java +++ b/src/main/java/nextstep/ladder/Position.java @@ -1,4 +1,36 @@ package nextstep.ladder; +import java.util.Objects; + public class Position { + public final Natural level; + public final Natural place; + + public Position(Natural level, Natural place) { + this.level = level; + this.place = place; + } + + public Position(long level, long place) { + this.level = new Natural(level); + this.place = new Natural(place); + } + + public Position getLeftPosition() { + return new Position(level.value(), place.value() - 1); + } + + @Override + public boolean equals(Object o) { + if (o instanceof Position) { + var that = (Position) o; + return level.equals(that.level) && place.equals(that.place); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hash(level, place); + } } diff --git a/src/main/java/nextstep/ladder/Users.java b/src/main/java/nextstep/ladder/Users.java index d0a85eca74..8efa830ea2 100644 --- a/src/main/java/nextstep/ladder/Users.java +++ b/src/main/java/nextstep/ladder/Users.java @@ -14,4 +14,8 @@ public Users(List users) { public Iterator iterator() { return users.iterator(); } + + public Natural size() { + return new Natural(users.size()); + } } diff --git a/src/test/java/nextstep/ladder/LadderTest.java b/src/test/java/nextstep/ladder/LadderTest.java index 367009829c..207a7b7ba9 100644 --- a/src/test/java/nextstep/ladder/LadderTest.java +++ b/src/test/java/nextstep/ladder/LadderTest.java @@ -4,17 +4,51 @@ import java.util.List; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; public class LadderTest { @Test public void levelAndUsersAreSet() { - var levels = 5; + var levels = new Natural(5); var users = new Users(List.of("erik", "jake", "david")); + var strategy = new AlwaysGenerateStrategy(); - var ladder = new Ladder(users, levels); + var ladder = new Ladder(users, levels, strategy); - assertThat(ladder.levels).isEqualTo(levels); + assertThat(ladder.height).isEqualTo(levels); assertThat(ladder.users).containsExactly("erik", "jake", "david"); } + + @Test + public void throwErrorOnNegativeInputs() { + assertThatThrownBy(() -> { + var levels = new Natural(-1); + var users = new Users(List.of()); + var strategy = new AlwaysGenerateStrategy(); + new Ladder(users, levels, strategy); + }).isInstanceOf(IllegalArgumentException.class) + .hasMessage("Negative value given for natural number"); + } + + @Test + public void legsAreNotPlacedConsecutively() { + var height = new Natural(5); + var users = new Users(List.of("erik", "jake", "david")); + var strategy = new AlwaysGenerateStrategy(); + + var ladder = new Ladder(users, height, strategy); + + for (int level = 0; level < height.value(); level++) { + assertThatLegsAreNotPlacedConsecutivelyInLevel(ladder, level, users.size().value()); + } + } + + private static void assertThatLegsAreNotPlacedConsecutivelyInLevel(Ladder ladder, long level, long width) { + for (int place = 1; place < width; place++) { + var previousLeg = ladder.legs.hasLegOnRightSide(new Position(level, place - 1)); + var currentLeg = ladder.legs.hasLegOnRightSide(new Position(level, place)); + assert !(previousLeg && currentLeg); + } + } } From 285e2acf3144df2af3a73c4119e7a635d8687828 Mon Sep 17 00:00:00 2001 From: "jake.b" Date: Thu, 25 May 2023 17:36:25 +0900 Subject: [PATCH 5/6] feature: implement view --- .../ladder/AlwaysGenerateStrategy.java | 9 --- .../nextstep/ladder/GenerationStrategy.java | 2 +- src/main/java/nextstep/ladder/Ladder.java | 4 +- .../nextstep/ladder/LadderController.java | 12 ++++ src/main/java/nextstep/ladder/LadderView.java | 65 +++++++++++++++++++ src/main/java/nextstep/ladder/Legs.java | 19 +++--- .../java/nextstep/ladder/NaturalInput.java | 20 ++++++ .../ladder/RandomGenerationStrategy.java | 11 ++++ src/main/java/nextstep/ladder/UsersInput.java | 15 +++++ src/test/java/nextstep/ladder/LadderTest.java | 13 ++-- .../nextstep/ladder/NaturalInputTest.java | 27 ++++++++ .../java/nextstep/ladder/UsersInputTest.java | 16 +++++ 12 files changed, 185 insertions(+), 28 deletions(-) delete mode 100644 src/main/java/nextstep/ladder/AlwaysGenerateStrategy.java create mode 100644 src/main/java/nextstep/ladder/LadderController.java create mode 100644 src/main/java/nextstep/ladder/LadderView.java create mode 100644 src/main/java/nextstep/ladder/NaturalInput.java create mode 100644 src/main/java/nextstep/ladder/RandomGenerationStrategy.java create mode 100644 src/main/java/nextstep/ladder/UsersInput.java create mode 100644 src/test/java/nextstep/ladder/NaturalInputTest.java create mode 100644 src/test/java/nextstep/ladder/UsersInputTest.java diff --git a/src/main/java/nextstep/ladder/AlwaysGenerateStrategy.java b/src/main/java/nextstep/ladder/AlwaysGenerateStrategy.java deleted file mode 100644 index a5e5b96cda..0000000000 --- a/src/main/java/nextstep/ladder/AlwaysGenerateStrategy.java +++ /dev/null @@ -1,9 +0,0 @@ -package nextstep.ladder; - -public class AlwaysGenerateStrategy implements GenerationStrategy{ - - @Override - public boolean shouldPlace(boolean isPlaceable) { - return isPlaceable; - } -} diff --git a/src/main/java/nextstep/ladder/GenerationStrategy.java b/src/main/java/nextstep/ladder/GenerationStrategy.java index e4d974c1e9..12d009a0e4 100644 --- a/src/main/java/nextstep/ladder/GenerationStrategy.java +++ b/src/main/java/nextstep/ladder/GenerationStrategy.java @@ -1,5 +1,5 @@ package nextstep.ladder; public interface GenerationStrategy { - boolean shouldPlace(boolean isPlaceable); + boolean shouldPlace(); } diff --git a/src/main/java/nextstep/ladder/Ladder.java b/src/main/java/nextstep/ladder/Ladder.java index 4f11ec2873..56a94ffbd0 100644 --- a/src/main/java/nextstep/ladder/Ladder.java +++ b/src/main/java/nextstep/ladder/Ladder.java @@ -5,8 +5,8 @@ public class Ladder { public final Users users; public final Legs legs; - public Ladder(Users users, Natural levels, GenerationStrategy strategy) { - this.height = levels; + public Ladder(Users users, Natural height, GenerationStrategy strategy) { + this.height = height; this.users = users; legs = new Legs(height, users.size(), strategy); } diff --git a/src/main/java/nextstep/ladder/LadderController.java b/src/main/java/nextstep/ladder/LadderController.java new file mode 100644 index 0000000000..ca12db9ca2 --- /dev/null +++ b/src/main/java/nextstep/ladder/LadderController.java @@ -0,0 +1,12 @@ +package nextstep.ladder; + +public class LadderController { + private static final LadderView view = new LadderView(); + public static void main(String[] args) { + var users = view.getUsers(); + var height = view.getHeight(); + + var ladder = new Ladder(users, height, new RandomGenerationStrategy()); + view.printResults(ladder); + } +} diff --git a/src/main/java/nextstep/ladder/LadderView.java b/src/main/java/nextstep/ladder/LadderView.java new file mode 100644 index 0000000000..7ad022ac7a --- /dev/null +++ b/src/main/java/nextstep/ladder/LadderView.java @@ -0,0 +1,65 @@ +package nextstep.ladder; + +import java.util.Scanner; + +public class LadderView { + public Users getUsers() { + System.out.println("참여할 사람 이름을 입력하세요. (이름은 쉼표(,)로 구분하세요)"); + var userInput = new UsersInput(getConsoleInput()); + return userInput.toUsers(); + } + + public Natural getHeight() { + System.out.println("최대 사다리 높이는 몇 개인가요?"); + var naturalInput = new NaturalInput(getConsoleInput()); + return naturalInput.toNatural(); + } + + public void printResults(Ladder ladder) { + System.out.println("실행결과"); + + ladder.users.forEach(user -> System.out.printf("%s\t", user)); + printNewLine(); + for (var level = ladder.height.value() - 1; level >= 0; level--) { + printLevel(level, ladder); + } + } + + private static void printLevel(long level, Ladder ladder) { + for (var place = 0; place < ladder.users.size().value() - 1; place++) { + printColumn(); + printLegIfExistsInLadder(new Position(level, place), ladder); + } + printColumn(); + printNewLine(); + } + + private static void printNewLine() { + System.out.println(); + } + + private static void printColumn() { + System.out.print("|"); + } + + private static void printLegIfExistsInLadder(Position position, Ladder ladder) { + if (ladder.legs.hasLegOnRightSideOf(position)) { + printFilledLeg(); + return; + } + printEmptyLeg(); + } + + private static void printEmptyLeg() { + System.out.print(" "); + } + + private static void printFilledLeg() { + System.out.print("-----"); + } + + private String getConsoleInput() { + Scanner scanner = new Scanner(System.in); + return scanner.nextLine(); + } +} diff --git a/src/main/java/nextstep/ladder/Legs.java b/src/main/java/nextstep/ladder/Legs.java index b7aa28b282..3c99244145 100644 --- a/src/main/java/nextstep/ladder/Legs.java +++ b/src/main/java/nextstep/ladder/Legs.java @@ -5,10 +5,10 @@ import java.util.stream.LongStream; public class Legs { - private final Set legs; + private final Set legLeftPositions; public Legs(Natural height, Natural width, GenerationStrategy strategy) { - legs = new HashSet<>(); + legLeftPositions = new HashSet<>(); placeLegs(height, width, strategy); } @@ -19,24 +19,27 @@ private void placeLegs(Natural height, Natural width, GenerationStrategy strateg } private void placeLegsInLevel(Natural level, Natural width, GenerationStrategy strategy) { - LongStream.range(0, width.value()) + LongStream.range(0, width.value() - 1) .mapToObj(Natural::new) .map(place -> new Position(level, place)) .forEach(position -> placeLegWithStrategy(strategy, position)); } private void placeLegWithStrategy(GenerationStrategy strategy, Position position) { - var isPlaceable = isOnTheLeftEdge(position) || !hasLegOnRightSide(position.getLeftPosition()); - if (strategy.shouldPlace(isPlaceable)) { - legs.add(position); + if (isLegPlaceableOnPosition(position) && strategy.shouldPlace()) { + legLeftPositions.add(position); } } + private boolean isLegPlaceableOnPosition(Position position) { + return isOnTheLeftEdge(position) || !hasLegOnRightSideOf(position.getLeftPosition()); + } + private boolean isOnTheLeftEdge(Position position) { return position.place.value() == 0; } - public boolean hasLegOnRightSide(Position position) { - return legs.contains(position); + public boolean hasLegOnRightSideOf(Position position) { + return legLeftPositions.contains(position); } } diff --git a/src/main/java/nextstep/ladder/NaturalInput.java b/src/main/java/nextstep/ladder/NaturalInput.java new file mode 100644 index 0000000000..3cfa096726 --- /dev/null +++ b/src/main/java/nextstep/ladder/NaturalInput.java @@ -0,0 +1,20 @@ +package nextstep.ladder; + +public class NaturalInput { + private final String input; + + public NaturalInput(String input) { + this.input = input; + checkInputIsNaturalNumber(); + } + + public Natural toNatural() { + return new Natural(Long.parseLong(input)); + } + + private void checkInputIsNaturalNumber() { + if (!input.matches("\\d+")) { + throw new IllegalArgumentException("Input is not a natural number"); + } + } +} diff --git a/src/main/java/nextstep/ladder/RandomGenerationStrategy.java b/src/main/java/nextstep/ladder/RandomGenerationStrategy.java new file mode 100644 index 0000000000..ae0fedbd9a --- /dev/null +++ b/src/main/java/nextstep/ladder/RandomGenerationStrategy.java @@ -0,0 +1,11 @@ +package nextstep.ladder; + +import java.util.Random; + +public class RandomGenerationStrategy implements GenerationStrategy { + private final Random random = new Random(); + @Override + public boolean shouldPlace() { + return random.nextBoolean(); + } +} diff --git a/src/main/java/nextstep/ladder/UsersInput.java b/src/main/java/nextstep/ladder/UsersInput.java new file mode 100644 index 0000000000..824150957c --- /dev/null +++ b/src/main/java/nextstep/ladder/UsersInput.java @@ -0,0 +1,15 @@ +package nextstep.ladder; + +import java.util.Arrays; + +public class UsersInput { + private final String input; + + public UsersInput(String input) { + this.input = input; + } + + public Users toUsers() { + return new Users(Arrays.asList(input.replace(" ", "").split(","))); + } +} diff --git a/src/test/java/nextstep/ladder/LadderTest.java b/src/test/java/nextstep/ladder/LadderTest.java index 207a7b7ba9..b168bce183 100644 --- a/src/test/java/nextstep/ladder/LadderTest.java +++ b/src/test/java/nextstep/ladder/LadderTest.java @@ -12,9 +12,8 @@ public class LadderTest { public void levelAndUsersAreSet() { var levels = new Natural(5); var users = new Users(List.of("erik", "jake", "david")); - var strategy = new AlwaysGenerateStrategy(); - var ladder = new Ladder(users, levels, strategy); + var ladder = new Ladder(users, levels, () -> true); assertThat(ladder.height).isEqualTo(levels); assertThat(ladder.users).containsExactly("erik", "jake", "david"); @@ -25,8 +24,7 @@ public void throwErrorOnNegativeInputs() { assertThatThrownBy(() -> { var levels = new Natural(-1); var users = new Users(List.of()); - var strategy = new AlwaysGenerateStrategy(); - new Ladder(users, levels, strategy); + new Ladder(users, levels, () -> true); }).isInstanceOf(IllegalArgumentException.class) .hasMessage("Negative value given for natural number"); } @@ -35,9 +33,8 @@ public void throwErrorOnNegativeInputs() { public void legsAreNotPlacedConsecutively() { var height = new Natural(5); var users = new Users(List.of("erik", "jake", "david")); - var strategy = new AlwaysGenerateStrategy(); - var ladder = new Ladder(users, height, strategy); + var ladder = new Ladder(users, height, () -> true); for (int level = 0; level < height.value(); level++) { assertThatLegsAreNotPlacedConsecutivelyInLevel(ladder, level, users.size().value()); @@ -46,8 +43,8 @@ public void legsAreNotPlacedConsecutively() { private static void assertThatLegsAreNotPlacedConsecutivelyInLevel(Ladder ladder, long level, long width) { for (int place = 1; place < width; place++) { - var previousLeg = ladder.legs.hasLegOnRightSide(new Position(level, place - 1)); - var currentLeg = ladder.legs.hasLegOnRightSide(new Position(level, place)); + var previousLeg = ladder.legs.hasLegOnRightSideOf(new Position(level, place - 1)); + var currentLeg = ladder.legs.hasLegOnRightSideOf(new Position(level, place)); assert !(previousLeg && currentLeg); } } diff --git a/src/test/java/nextstep/ladder/NaturalInputTest.java b/src/test/java/nextstep/ladder/NaturalInputTest.java new file mode 100644 index 0000000000..628b19be1b --- /dev/null +++ b/src/test/java/nextstep/ladder/NaturalInputTest.java @@ -0,0 +1,27 @@ +package nextstep.ladder; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; +import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; + +public class NaturalInputTest { + @ParameterizedTest + @ValueSource(strings = {"12wr23", " "}) + public void throwErrorOnInvalidInputs(String input) { + assertThatThrownBy(() -> new NaturalInput(input)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Input is not a natural number"); + } + + @Test + public void convertsInputToNatural() { + var input = "5"; + + var natural = new NaturalInput(input).toNatural(); + + assertThat(natural.value()).isEqualTo(5L); + } +} diff --git a/src/test/java/nextstep/ladder/UsersInputTest.java b/src/test/java/nextstep/ladder/UsersInputTest.java new file mode 100644 index 0000000000..e36f69fe41 --- /dev/null +++ b/src/test/java/nextstep/ladder/UsersInputTest.java @@ -0,0 +1,16 @@ +package nextstep.ladder; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; + +public class UsersInputTest { + @Test + public void parseCommaSplicedUserInput() { + var input = "pobi, honux, crong, jk"; + + var userInput = new UsersInput(input); + + assertThat(userInput.toUsers()).containsExactly("pobi", "honux", "crong", "jk"); + } +} From e286dd20d7ce925fbbc6be000c10c92760c3a2d2 Mon Sep 17 00:00:00 2001 From: "jake.b" Date: Thu, 25 May 2023 17:43:34 +0900 Subject: [PATCH 6/6] refactor: refactored ladder unit test --- src/test/java/nextstep/ladder/LadderTest.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/test/java/nextstep/ladder/LadderTest.java b/src/test/java/nextstep/ladder/LadderTest.java index b168bce183..f0266cf09e 100644 --- a/src/test/java/nextstep/ladder/LadderTest.java +++ b/src/test/java/nextstep/ladder/LadderTest.java @@ -43,9 +43,10 @@ public void legsAreNotPlacedConsecutively() { private static void assertThatLegsAreNotPlacedConsecutivelyInLevel(Ladder ladder, long level, long width) { for (int place = 1; place < width; place++) { - var previousLeg = ladder.legs.hasLegOnRightSideOf(new Position(level, place - 1)); - var currentLeg = ladder.legs.hasLegOnRightSideOf(new Position(level, place)); - assert !(previousLeg && currentLeg); + var currentPosition = new Position(level, place); + var currentLeg = ladder.legs.hasLegOnRightSideOf(currentPosition); + var leftLeg = ladder.legs.hasLegOnRightSideOf(currentPosition.getLeftPosition()); + assert !(leftLeg && currentLeg); } } }