From dfd56b67a0ffad782e62ba2628635a1dab2adc2c Mon Sep 17 00:00:00 2001 From: seriouskang Date: Sat, 13 May 2023 22:32:22 +0900 Subject: [PATCH 01/17] =?UTF-8?q?feat(Line):=20=EC=82=AC=EB=8B=A4=EB=A6=AC?= =?UTF-8?q?=EC=9D=98=20=EA=B0=80=EB=A1=9C=20=EB=9D=BC=EC=9D=B8=EC=97=90=20?= =?UTF-8?q?=EB=8C=80=ED=95=9C=20=EB=8F=84=EB=A9=94=EC=9D=B8=20=EA=B0=9D?= =?UTF-8?q?=EC=B2=B4=20=EC=8B=A0=EA=B7=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/nextstep/ladder/domain/Line.java | 33 ++++++++++++ .../java/nextstep/ladder/domain/LineTest.java | 53 +++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 src/main/java/nextstep/ladder/domain/Line.java create mode 100644 src/test/java/nextstep/ladder/domain/LineTest.java diff --git a/src/main/java/nextstep/ladder/domain/Line.java b/src/main/java/nextstep/ladder/domain/Line.java new file mode 100644 index 0000000000..95271d3dba --- /dev/null +++ b/src/main/java/nextstep/ladder/domain/Line.java @@ -0,0 +1,33 @@ +package nextstep.ladder.domain; + +import java.util.List; +import java.util.stream.IntStream; + +public class Line { + private static final int START_INDEX_OF_CONNECTIONS = 0; + private List connections; + + public Line(List connections) { + validateConnections(connections); + this.connections = connections; + } + + private void validateConnections(List connections) { + if(containsConsecutiveConnections(connections)) { + throw new IllegalArgumentException("가로 라인의 연결선은 연속해서 존재할 수 없습니다."); + } + } + + private boolean containsConsecutiveConnections(List connections) { + return IntStream.range(START_INDEX_OF_CONNECTIONS, beforeLastIndexOf(connections)) + .anyMatch(index -> isConsecutiveConnections(connections, index)); + } + + private int beforeLastIndexOf(List connections) { + return connections.size() - 1; + } + + private boolean isConsecutiveConnections(List connections, int index) { + return connections.get(index) && connections.get(index + 1); + } +} diff --git a/src/test/java/nextstep/ladder/domain/LineTest.java b/src/test/java/nextstep/ladder/domain/LineTest.java new file mode 100644 index 0000000000..dac20f2281 --- /dev/null +++ b/src/test/java/nextstep/ladder/domain/LineTest.java @@ -0,0 +1,53 @@ +package nextstep.ladder.domain; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.List; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.*; + +class LineTest { + @ParameterizedTest(name = "[{index}/3] {displayName}") + @MethodSource("invalidConnections") + @DisplayName("가로 라인의 연결선이 연속해서 존재할 경우, IllegalArgumentException 예외 발생") + void invalid_connections(List invalidConnections) { + assertThatThrownBy(() -> new Line(invalidConnections)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("가로 라인의 연결선은 연속해서 존재할 수 없습니다."); + } + + @ParameterizedTest(name = "[{index}/9] {displayName}") + @MethodSource("validConnections") + @DisplayName("가로 라인 연견설이 연속해서 존재하지 않을 경우, Line 객체 생성") + void valid_connections(List validConnections) { + assertThat(new Line(validConnections)) + .isInstanceOf(Line.class); + } + + static Stream invalidConnections() { + return Stream.of( + Arguments.arguments(List.of(true, true)), + Arguments.arguments(List.of(false, true, true)), + Arguments.arguments(List.of(true, true, false)) + ); + } + + static Stream validConnections() { + return Stream.of( + Arguments.arguments(List.of()), + Arguments.arguments(List.of(false)), + Arguments.arguments(List.of(true)), + Arguments.arguments(List.of(true, false)), + Arguments.arguments(List.of(false, true)), + Arguments.arguments(List.of(true, false, false)), + Arguments.arguments(List.of(false, true, false)), + Arguments.arguments(List.of(false, false, true)), + Arguments.arguments(List.of(true, false, true)) + ); + } +} \ No newline at end of file From c707bc4a4f69081ffd052425fe8f49915d991c53 Mon Sep 17 00:00:00 2001 From: seriouskang Date: Sat, 13 May 2023 22:52:50 +0900 Subject: [PATCH 02/17] =?UTF-8?q?feat(Line):=20=EA=B0=80=EB=A1=9C=20?= =?UTF-8?q?=EC=97=B0=EA=B2=B0=EC=84=A0=EC=9D=B4=20null=EC=9D=BC=20?= =?UTF-8?q?=EA=B2=BD=EC=9A=B0=EC=97=90=20=EB=8C=80=ED=95=9C=20=EA=B2=80?= =?UTF-8?q?=EC=A6=9D=20=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/nextstep/ladder/domain/Line.java | 4 ++++ .../java/nextstep/ladder/domain/LineTest.java | 16 ++++++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/main/java/nextstep/ladder/domain/Line.java b/src/main/java/nextstep/ladder/domain/Line.java index 95271d3dba..d62fd1a174 100644 --- a/src/main/java/nextstep/ladder/domain/Line.java +++ b/src/main/java/nextstep/ladder/domain/Line.java @@ -13,6 +13,10 @@ public Line(List connections) { } private void validateConnections(List connections) { + if(connections == null) { + throw new IllegalArgumentException("가로 라인의 연결선은 null이 될 수 없습니다."); + } + if(containsConsecutiveConnections(connections)) { throw new IllegalArgumentException("가로 라인의 연결선은 연속해서 존재할 수 없습니다."); } diff --git a/src/test/java/nextstep/ladder/domain/LineTest.java b/src/test/java/nextstep/ladder/domain/LineTest.java index dac20f2281..8c5276ef18 100644 --- a/src/test/java/nextstep/ladder/domain/LineTest.java +++ b/src/test/java/nextstep/ladder/domain/LineTest.java @@ -13,10 +13,10 @@ class LineTest { @ParameterizedTest(name = "[{index}/3] {displayName}") - @MethodSource("invalidConnections") + @MethodSource("consecutiveConnections") @DisplayName("가로 라인의 연결선이 연속해서 존재할 경우, IllegalArgumentException 예외 발생") - void invalid_connections(List invalidConnections) { - assertThatThrownBy(() -> new Line(invalidConnections)) + void consecutive_connections_then_throw_IllegalArgumentException(List consecutiveConnections) { + assertThatThrownBy(() -> new Line(consecutiveConnections)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("가로 라인의 연결선은 연속해서 존재할 수 없습니다."); } @@ -29,7 +29,15 @@ void valid_connections(List validConnections) { .isInstanceOf(Line.class); } - static Stream invalidConnections() { + @Test + @DisplayName("가로 라인의 연결선이 null일 경우, IllegalArgumentException 예외 발생") + void null_connections_then_throw_IllegalArgumentException() { + assertThatThrownBy(() -> new Line(null)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("가로 라인의 연결선은 null이 될 수 없습니다."); + } + + static Stream consecutiveConnections() { return Stream.of( Arguments.arguments(List.of(true, true)), Arguments.arguments(List.of(false, true, true)), From 69ed35baadec1deb0675696cfbde76427d231036 Mon Sep 17 00:00:00 2001 From: seriouskang Date: Sun, 14 May 2023 00:40:43 +0900 Subject: [PATCH 03/17] =?UTF-8?q?feat(Connections):=20=EA=B0=80=EB=A1=9C?= =?UTF-8?q?=20=EB=9D=BC=EC=9D=B8=20=EC=97=B0=EA=B2=B0=EC=84=A0=20=ED=8F=AC?= =?UTF-8?q?=EC=9E=A5=20=EA=B0=9D=EC=B2=B4=20=EC=8B=A0=EA=B7=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nextstep/ladder/domain/Connections.java | 37 +++++++++++++++++++ .../java/nextstep/ladder/domain/Line.java | 32 +--------------- .../{LineTest.java => ConnectionsTest.java} | 12 +++--- 3 files changed, 45 insertions(+), 36 deletions(-) create mode 100644 src/main/java/nextstep/ladder/domain/Connections.java rename src/test/java/nextstep/ladder/domain/{LineTest.java => ConnectionsTest.java} (88%) diff --git a/src/main/java/nextstep/ladder/domain/Connections.java b/src/main/java/nextstep/ladder/domain/Connections.java new file mode 100644 index 0000000000..d35738fbcf --- /dev/null +++ b/src/main/java/nextstep/ladder/domain/Connections.java @@ -0,0 +1,37 @@ +package nextstep.ladder.domain; + +import java.util.List; +import java.util.stream.IntStream; + +public class Connections { + private static final int START_INDEX_OF_CONNECTIONS = 0; + private final List connections; + + public Connections(List connections) { + validateConnections(connections); + this.connections = connections; + } + + private void validateConnections(List connections) { + if(connections == null) { + throw new IllegalArgumentException("가로 라인의 연결선은 null이 될 수 없습니다."); + } + + if(containsConsecutiveConnections(connections)) { + throw new IllegalArgumentException("가로 라인의 연결선은 연속해서 존재할 수 없습니다."); + } + } + + private boolean containsConsecutiveConnections(List connections) { + return IntStream.range(START_INDEX_OF_CONNECTIONS, beforeLastIndexOf(connections)) + .anyMatch(index -> isConsecutiveConnections(connections, index)); + } + + private int beforeLastIndexOf(List connections) { + return connections.size() - 1; + } + + private boolean isConsecutiveConnections(List connections, int index) { + return connections.get(index) && connections.get(index + 1); + } +} diff --git a/src/main/java/nextstep/ladder/domain/Line.java b/src/main/java/nextstep/ladder/domain/Line.java index d62fd1a174..9dc430b996 100644 --- a/src/main/java/nextstep/ladder/domain/Line.java +++ b/src/main/java/nextstep/ladder/domain/Line.java @@ -1,37 +1,9 @@ package nextstep.ladder.domain; -import java.util.List; -import java.util.stream.IntStream; - public class Line { - private static final int START_INDEX_OF_CONNECTIONS = 0; - private List connections; + private final Connections connections; - public Line(List connections) { - validateConnections(connections); + public Line(Connections connections) { this.connections = connections; } - - private void validateConnections(List connections) { - if(connections == null) { - throw new IllegalArgumentException("가로 라인의 연결선은 null이 될 수 없습니다."); - } - - if(containsConsecutiveConnections(connections)) { - throw new IllegalArgumentException("가로 라인의 연결선은 연속해서 존재할 수 없습니다."); - } - } - - private boolean containsConsecutiveConnections(List connections) { - return IntStream.range(START_INDEX_OF_CONNECTIONS, beforeLastIndexOf(connections)) - .anyMatch(index -> isConsecutiveConnections(connections, index)); - } - - private int beforeLastIndexOf(List connections) { - return connections.size() - 1; - } - - private boolean isConsecutiveConnections(List connections, int index) { - return connections.get(index) && connections.get(index + 1); - } } diff --git a/src/test/java/nextstep/ladder/domain/LineTest.java b/src/test/java/nextstep/ladder/domain/ConnectionsTest.java similarity index 88% rename from src/test/java/nextstep/ladder/domain/LineTest.java rename to src/test/java/nextstep/ladder/domain/ConnectionsTest.java index 8c5276ef18..9743a671cc 100644 --- a/src/test/java/nextstep/ladder/domain/LineTest.java +++ b/src/test/java/nextstep/ladder/domain/ConnectionsTest.java @@ -11,28 +11,28 @@ import static org.assertj.core.api.Assertions.*; -class LineTest { +class ConnectionsTest { @ParameterizedTest(name = "[{index}/3] {displayName}") @MethodSource("consecutiveConnections") @DisplayName("가로 라인의 연결선이 연속해서 존재할 경우, IllegalArgumentException 예외 발생") void consecutive_connections_then_throw_IllegalArgumentException(List consecutiveConnections) { - assertThatThrownBy(() -> new Line(consecutiveConnections)) + assertThatThrownBy(() -> new Connections(consecutiveConnections)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("가로 라인의 연결선은 연속해서 존재할 수 없습니다."); } @ParameterizedTest(name = "[{index}/9] {displayName}") @MethodSource("validConnections") - @DisplayName("가로 라인 연견설이 연속해서 존재하지 않을 경우, Line 객체 생성") + @DisplayName("가로 라인 연견설이 연속해서 존재하지 않을 경우, Connections 객체 생성") void valid_connections(List validConnections) { - assertThat(new Line(validConnections)) - .isInstanceOf(Line.class); + assertThat(new Connections(validConnections)) + .isInstanceOf(Connections.class); } @Test @DisplayName("가로 라인의 연결선이 null일 경우, IllegalArgumentException 예외 발생") void null_connections_then_throw_IllegalArgumentException() { - assertThatThrownBy(() -> new Line(null)) + assertThatThrownBy(() -> new Connections(null)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("가로 라인의 연결선은 null이 될 수 없습니다."); } From 3216b24cffc45d7132a09cb243ce1857e0e3cd63 Mon Sep 17 00:00:00 2001 From: seriouskang Date: Sun, 14 May 2023 01:31:24 +0900 Subject: [PATCH 04/17] =?UTF-8?q?feat(LineFactory,=20ConnectionsFactory):?= =?UTF-8?q?=20=EB=9D=BC=EC=9D=B8=20=ED=8C=A9=ED=86=A0=EB=A6=AC=20=EB=B0=8F?= =?UTF-8?q?=20Connections=20=ED=8C=A9=ED=86=A0=EB=A6=AC=20=EC=8B=A0?= =?UTF-8?q?=EA=B7=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nextstep/ladder/domain/Connections.java | 19 +++++++- .../ladder/domain/ConnectionsFactory.java | 5 ++ .../java/nextstep/ladder/domain/Line.java | 25 ++++++++++ .../nextstep/ladder/domain/LineFactory.java | 25 ++++++++++ .../ladder/domain/LineFactoryTest.java | 48 +++++++++++++++++++ 5 files changed, 120 insertions(+), 2 deletions(-) create mode 100644 src/main/java/nextstep/ladder/domain/ConnectionsFactory.java create mode 100644 src/main/java/nextstep/ladder/domain/LineFactory.java create mode 100644 src/test/java/nextstep/ladder/domain/LineFactoryTest.java diff --git a/src/main/java/nextstep/ladder/domain/Connections.java b/src/main/java/nextstep/ladder/domain/Connections.java index d35738fbcf..ee5cdeae55 100644 --- a/src/main/java/nextstep/ladder/domain/Connections.java +++ b/src/main/java/nextstep/ladder/domain/Connections.java @@ -1,10 +1,12 @@ package nextstep.ladder.domain; import java.util.List; +import java.util.Objects; import java.util.stream.IntStream; public class Connections { private static final int START_INDEX_OF_CONNECTIONS = 0; + private static final int ONE = 1; private final List connections; public Connections(List connections) { @@ -28,10 +30,23 @@ private boolean containsConsecutiveConnections(List connections) { } private int beforeLastIndexOf(List connections) { - return connections.size() - 1; + return connections.size() - ONE; } private boolean isConsecutiveConnections(List connections, int index) { - return connections.get(index) && connections.get(index + 1); + return connections.get(index) && connections.get(index + ONE); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Connections)) return false; + Connections that = (Connections) o; + return Objects.equals(connections, that.connections); + } + + @Override + public int hashCode() { + return Objects.hash(connections); } } diff --git a/src/main/java/nextstep/ladder/domain/ConnectionsFactory.java b/src/main/java/nextstep/ladder/domain/ConnectionsFactory.java new file mode 100644 index 0000000000..3af4db52ea --- /dev/null +++ b/src/main/java/nextstep/ladder/domain/ConnectionsFactory.java @@ -0,0 +1,5 @@ +package nextstep.ladder.domain; + +public interface ConnectionsFactory { + Connections create(int numberOfConnections); +} diff --git a/src/main/java/nextstep/ladder/domain/Line.java b/src/main/java/nextstep/ladder/domain/Line.java index 9dc430b996..128dc40912 100644 --- a/src/main/java/nextstep/ladder/domain/Line.java +++ b/src/main/java/nextstep/ladder/domain/Line.java @@ -1,9 +1,34 @@ package nextstep.ladder.domain; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + public class Line { private final Connections connections; + public Line(Boolean...connections) { + this(Arrays.asList(connections)); + } + + public Line(List connections) { + this(new Connections(connections)); + } + public Line(Connections connections) { this.connections = connections; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Line)) return false; + Line line = (Line) o; + return Objects.equals(connections, line.connections); + } + + @Override + public int hashCode() { + return Objects.hash(connections); + } } diff --git a/src/main/java/nextstep/ladder/domain/LineFactory.java b/src/main/java/nextstep/ladder/domain/LineFactory.java new file mode 100644 index 0000000000..584727dd90 --- /dev/null +++ b/src/main/java/nextstep/ladder/domain/LineFactory.java @@ -0,0 +1,25 @@ +package nextstep.ladder.domain; + +public class LineFactory { + private static final int ZERO = 0; + private final ConnectionsFactory connectionsFactory; + + public LineFactory(ConnectionsFactory connectionsFactory) { + this.connectionsFactory = connectionsFactory; + } + + public Line create(int numberOfConnections) { + validateNumberOfConnections(numberOfConnections); + return new Line(connections(numberOfConnections)); + } + + private Connections connections(int numberOfConnections) { + return connectionsFactory.create(numberOfConnections); + } + + private void validateNumberOfConnections(int numberOfConnections) { + if(numberOfConnections < ZERO) { + throw new IllegalArgumentException("라인 연결수는 0 미만이 될 수 없습니다: " + numberOfConnections); + } + } +} diff --git a/src/test/java/nextstep/ladder/domain/LineFactoryTest.java b/src/test/java/nextstep/ladder/domain/LineFactoryTest.java new file mode 100644 index 0000000000..b3b149353d --- /dev/null +++ b/src/test/java/nextstep/ladder/domain/LineFactoryTest.java @@ -0,0 +1,48 @@ +package nextstep.ladder.domain; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import static org.assertj.core.api.Assertions.*; + +class LineFactoryTest { + @Test + @DisplayName("Line 객체 생성") + void create() { + // given + LineFactory lineFactory = new LineFactory(evenConnectedConnectionsFactory()); + + // when + Line line = lineFactory.create(4); + + // then + assertThat(line).isEqualTo(new Line(true, false, true, false)); + } + + @Test + @DisplayName("0보다 작은 라인의 연결 갯수로 Line 객체 생성 시, IllegalArgumentException 예외 발생") + void invalid_number_of_connections() { + // given + LineFactory lineFactory = new LineFactory(evenConnectedConnectionsFactory()); + int invalidNumberOfConnections = -1; + + // when, then + assertThatThrownBy(() -> lineFactory.create(invalidNumberOfConnections)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("라인 연결수는 0 미만이 될 수 없습니다: " + invalidNumberOfConnections); + } + + private ConnectionsFactory evenConnectedConnectionsFactory() { + return numberOfConnections -> new Connections(evenElementTrueList(numberOfConnections)); + } + + private List evenElementTrueList(int size) { + return IntStream.range(0, size) + .mapToObj(index -> index % 2 == 0) + .collect(Collectors.toUnmodifiableList()); + } +} \ No newline at end of file From db7d3801ef0f5bd2750d40b8a87007022f1165b2 Mon Sep 17 00:00:00 2001 From: seriouskang Date: Sun, 14 May 2023 02:11:39 +0900 Subject: [PATCH 05/17] =?UTF-8?q?feat(Ladder,=20LadderFactory):=20?= =?UTF-8?q?=EC=82=AC=EB=8B=A4=EB=A6=AC=20=EB=B0=8F=20=EC=82=AC=EB=8B=A4?= =?UTF-8?q?=EB=A6=AC=20=ED=8C=A9=ED=86=A0=EB=A6=AC=20=ED=81=B4=EB=9E=98?= =?UTF-8?q?=EC=8A=A4=20=EC=8B=A0=EA=B7=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/nextstep/ladder/domain/Ladder.java | 30 ++++++++++ .../nextstep/ladder/domain/LadderFactory.java | 36 ++++++++++++ .../ladder/domain/LadderFactoryTest.java | 58 +++++++++++++++++++ 3 files changed, 124 insertions(+) create mode 100644 src/main/java/nextstep/ladder/domain/Ladder.java create mode 100644 src/main/java/nextstep/ladder/domain/LadderFactory.java create mode 100644 src/test/java/nextstep/ladder/domain/LadderFactoryTest.java diff --git a/src/main/java/nextstep/ladder/domain/Ladder.java b/src/main/java/nextstep/ladder/domain/Ladder.java new file mode 100644 index 0000000000..c0e2c61323 --- /dev/null +++ b/src/main/java/nextstep/ladder/domain/Ladder.java @@ -0,0 +1,30 @@ +package nextstep.ladder.domain; + +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +public class Ladder { + private final List lines; + + public Ladder(Line...lines) { + this(Arrays.asList(lines)); + } + + public Ladder(List lines) { + this.lines = lines; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Ladder)) return false; + Ladder ladder = (Ladder) o; + return Objects.equals(lines, ladder.lines); + } + + @Override + public int hashCode() { + return Objects.hash(lines); + } +} diff --git a/src/main/java/nextstep/ladder/domain/LadderFactory.java b/src/main/java/nextstep/ladder/domain/LadderFactory.java new file mode 100644 index 0000000000..81810998bd --- /dev/null +++ b/src/main/java/nextstep/ladder/domain/LadderFactory.java @@ -0,0 +1,36 @@ +package nextstep.ladder.domain; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +public class LadderFactory { + private static final int ONE = 1; + private static final int ZERO = 0; + private final LineFactory lineFactory; + + public LadderFactory(LineFactory lineFactory) { + this.lineFactory = lineFactory; + } + + public Ladder create(int numberOfLine, int height) { + validateHeight(height); + return new Ladder(lines(numberOfLine, height)); + } + + private List lines(int numberOfLine, int height) { + return IntStream.range(ZERO, height) + .mapToObj(index -> lineFactory.create(numberOfConnections(numberOfLine))) + .collect(Collectors.toUnmodifiableList()); + } + + private int numberOfConnections(int numberOfLine) { + return numberOfLine - ONE; + } + + private void validateHeight(int height) { + if(height <= ZERO) { + throw new IllegalArgumentException("사다리의 높이는 0 이하일 수 없습니다: " + height); + } + } +} diff --git a/src/test/java/nextstep/ladder/domain/LadderFactoryTest.java b/src/test/java/nextstep/ladder/domain/LadderFactoryTest.java new file mode 100644 index 0000000000..017f6a107b --- /dev/null +++ b/src/test/java/nextstep/ladder/domain/LadderFactoryTest.java @@ -0,0 +1,58 @@ +package nextstep.ladder.domain; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import static org.assertj.core.api.Assertions.*; + +class LadderFactoryTest { + @Test + @DisplayName("0 이하의 높이로 사다리 생성 시, IllegalArgumentException 예외 발생") + void invalid_height_then_throw_IllegalArgumentException() { + // given + LadderFactory ladderFactory = new LadderFactory(evenConnectedLineFactory()); + int invalidHeight = 0; + + // when, then + assertThatThrownBy(() -> ladderFactory.create(5, invalidHeight)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("사다리의 높이는 0 이하일 수 없습니다: " + invalidHeight); + } + + @Test + @DisplayName("ladder 객체 생성") + void create_ladder_object() { + // given + LadderFactory ladderFactory = new LadderFactory(evenConnectedLineFactory()); + int height = 3; + int numberOfLine = 4; + + // when + Ladder ladder = ladderFactory.create(numberOfLine, height); + + // then + assertThat(ladder).isEqualTo(new Ladder( + new Line(true, false, true), + new Line(true, false, true), + new Line(true, false, true)) + ); + } + + private LineFactory evenConnectedLineFactory() { + return new LineFactory(evenConnectedConnectionsFactory()); + } + + private ConnectionsFactory evenConnectedConnectionsFactory() { + return numberOfConnections -> new Connections(evenElementTrueList(numberOfConnections)); + } + + private List evenElementTrueList(int size) { + return IntStream.range(0, size) + .mapToObj(index -> index % 2 == 0) + .collect(Collectors.toUnmodifiableList()); + } +} \ No newline at end of file From c80cf450ab4e472301577967aee504d96bf50670 Mon Sep 17 00:00:00 2001 From: seriouskang Date: Sun, 14 May 2023 10:41:20 +0900 Subject: [PATCH 06/17] =?UTF-8?q?feat(Name):=20=EC=82=AC=EB=8B=A4=EB=A6=AC?= =?UTF-8?q?=20=EA=B2=8C=EC=9E=84=20=EC=B0=B8=EC=97=AC=EC=9E=90=20=EC=9D=B4?= =?UTF-8?q?=EB=A6=84=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=8B=A0=EA=B7=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ladder/domain/ParticipantName.java | 21 +++++++++++++ .../ladder/domain/ParticipantNameTest.java | 30 +++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 src/main/java/nextstep/ladder/domain/ParticipantName.java create mode 100644 src/test/java/nextstep/ladder/domain/ParticipantNameTest.java diff --git a/src/main/java/nextstep/ladder/domain/ParticipantName.java b/src/main/java/nextstep/ladder/domain/ParticipantName.java new file mode 100644 index 0000000000..f90d6c2aed --- /dev/null +++ b/src/main/java/nextstep/ladder/domain/ParticipantName.java @@ -0,0 +1,21 @@ +package nextstep.ladder.domain; + +public class ParticipantName { + private static final int MAX_NAME_LENGTH = 5; + private final String name; + + public ParticipantName(String name) { + validateName(name); + this.name = name; + } + + private void validateName(String name) { + if(name == null || name.isBlank()) { + throw new IllegalArgumentException("이름은 null이거나 공백일 수 없습니다: " + name); + } + + if(name.length() > MAX_NAME_LENGTH) { + throw new IllegalArgumentException("이름의 길이는 5를 초과할 수 없습니다: " + name.length()); + } + } +} diff --git a/src/test/java/nextstep/ladder/domain/ParticipantNameTest.java b/src/test/java/nextstep/ladder/domain/ParticipantNameTest.java new file mode 100644 index 0000000000..1e869081a3 --- /dev/null +++ b/src/test/java/nextstep/ladder/domain/ParticipantNameTest.java @@ -0,0 +1,30 @@ +package nextstep.ladder.domain; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullAndEmptySource; + +import static org.assertj.core.api.Assertions.*; + +class ParticipantNameTest { + @ParameterizedTest(name = "[{index}/2] {displayName}") + @NullAndEmptySource + @DisplayName("null이거나 공백의 이름으로 Name 객체 생성 시, IllegalArgumentException 예외 발생") + void null_or_blank_name_then_throw_IllegalArgumentException(String invalidName) { + assertThatThrownBy(() -> new ParticipantName(invalidName)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("이름은 null이거나 공백일 수 없습니다: " + invalidName); + } + + @Test + void more_than_5_characters_then_throw_IllegalArgumentException() { + // given + String moreThanFiveCharactersName = "abcdef"; + + // when, then + assertThatThrownBy(() -> new ParticipantName(moreThanFiveCharactersName)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("이름의 길이는 5를 초과할 수 없습니다: " + moreThanFiveCharactersName.length()); + } +} \ No newline at end of file From e158913db7d8d894c95c3034d203e6e9ba6e6f0c Mon Sep 17 00:00:00 2001 From: seriouskang Date: Sun, 14 May 2023 10:54:37 +0900 Subject: [PATCH 07/17] =?UTF-8?q?feat(Participants):=20=EC=B0=B8=EC=97=AC?= =?UTF-8?q?=EC=9E=90=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=8B=A0=EA=B7=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ladder/domain/ParticipantName.java | 17 +++++++- .../nextstep/ladder/domain/Participants.java | 43 +++++++++++++++++++ .../ladder/domain/ParticipantNameTest.java | 2 +- .../ladder/domain/ParticipantsTest.java | 22 ++++++++++ 4 files changed, 82 insertions(+), 2 deletions(-) create mode 100644 src/main/java/nextstep/ladder/domain/Participants.java create mode 100644 src/test/java/nextstep/ladder/domain/ParticipantsTest.java diff --git a/src/main/java/nextstep/ladder/domain/ParticipantName.java b/src/main/java/nextstep/ladder/domain/ParticipantName.java index f90d6c2aed..b7b1d8aa5c 100644 --- a/src/main/java/nextstep/ladder/domain/ParticipantName.java +++ b/src/main/java/nextstep/ladder/domain/ParticipantName.java @@ -1,5 +1,7 @@ package nextstep.ladder.domain; +import java.util.Objects; + public class ParticipantName { private static final int MAX_NAME_LENGTH = 5; private final String name; @@ -15,7 +17,20 @@ private void validateName(String name) { } if(name.length() > MAX_NAME_LENGTH) { - throw new IllegalArgumentException("이름의 길이는 5를 초과할 수 없습니다: " + name.length()); + throw new IllegalArgumentException("이름의 길이는 5를 초과할 수 없습니다: " + name); } } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof ParticipantName)) return false; + ParticipantName that = (ParticipantName) o; + return Objects.equals(name, that.name); + } + + @Override + public int hashCode() { + return Objects.hash(name); + } } diff --git a/src/main/java/nextstep/ladder/domain/Participants.java b/src/main/java/nextstep/ladder/domain/Participants.java new file mode 100644 index 0000000000..4574768208 --- /dev/null +++ b/src/main/java/nextstep/ladder/domain/Participants.java @@ -0,0 +1,43 @@ +package nextstep.ladder.domain; + +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +public class Participants { + private static final String COMMA = ","; + private final List names; + + public Participants(String csvNames) { + this(csvNames.split(COMMA)); + } + + public Participants(String...names) { + this(participantsNames(names)); + } + + public Participants(List names) { + this.names = names; + } + + private static List participantsNames(String[] names) { + return Arrays.stream(names) + .map(String::trim) + .map(ParticipantName::new) + .collect(Collectors.toUnmodifiableList()); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Participants)) return false; + Participants that = (Participants) o; + return Objects.equals(names, that.names); + } + + @Override + public int hashCode() { + return Objects.hash(names); + } +} diff --git a/src/test/java/nextstep/ladder/domain/ParticipantNameTest.java b/src/test/java/nextstep/ladder/domain/ParticipantNameTest.java index 1e869081a3..d4b3051ee9 100644 --- a/src/test/java/nextstep/ladder/domain/ParticipantNameTest.java +++ b/src/test/java/nextstep/ladder/domain/ParticipantNameTest.java @@ -25,6 +25,6 @@ void more_than_5_characters_then_throw_IllegalArgumentException() { // when, then assertThatThrownBy(() -> new ParticipantName(moreThanFiveCharactersName)) .isInstanceOf(IllegalArgumentException.class) - .hasMessage("이름의 길이는 5를 초과할 수 없습니다: " + moreThanFiveCharactersName.length()); + .hasMessage("이름의 길이는 5를 초과할 수 없습니다: " + moreThanFiveCharactersName); } } \ No newline at end of file diff --git a/src/test/java/nextstep/ladder/domain/ParticipantsTest.java b/src/test/java/nextstep/ladder/domain/ParticipantsTest.java new file mode 100644 index 0000000000..83a6c10dc4 --- /dev/null +++ b/src/test/java/nextstep/ladder/domain/ParticipantsTest.java @@ -0,0 +1,22 @@ +package nextstep.ladder.domain; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.*; + +class ParticipantsTest { + @Test + @DisplayName("csv 문자열로 입력된 사용자 이름으로 Participants 객체 생성") + void create_by_CSV_String() { + // given + String participantsName = "foo, bar , baz"; + + // when + Participants participants = new Participants(participantsName); + + // then + Participants expectedParticipants = new Participants("foo", "bar", "baz"); + assertThat(participants).isEqualTo(expectedParticipants); + } +} \ No newline at end of file From 14318fc52c7dd7b9e321c84bc79b82d008c1b4ba Mon Sep 17 00:00:00 2001 From: seriouskang Date: Sun, 14 May 2023 12:21:47 +0900 Subject: [PATCH 08/17] =?UTF-8?q?feat(ConsoleInputUtil):=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=EC=9E=90=20=EC=BD=98=EC=86=94=20=EC=9E=85=EB=A0=A5?= =?UTF-8?q?=EC=9A=A9=20=EC=9C=A0=ED=8B=B8=20=ED=81=B4=EB=9E=98=EC=8A=A4=20?= =?UTF-8?q?=EC=8B=A0=EA=B7=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nextstep/ladder/domain/LadderFactory.java | 4 ++++ .../nextstep/ladder/domain/Participants.java | 4 ++++ .../ladder/presentation/PromptMessage.java | 17 ++++++++++++++ .../presentation/util/ConsoleInputUtil.java | 22 +++++++++++++++++++ 4 files changed, 47 insertions(+) create mode 100644 src/main/java/nextstep/ladder/presentation/PromptMessage.java create mode 100644 src/main/java/nextstep/ladder/presentation/util/ConsoleInputUtil.java diff --git a/src/main/java/nextstep/ladder/domain/LadderFactory.java b/src/main/java/nextstep/ladder/domain/LadderFactory.java index 81810998bd..3b94ca5991 100644 --- a/src/main/java/nextstep/ladder/domain/LadderFactory.java +++ b/src/main/java/nextstep/ladder/domain/LadderFactory.java @@ -13,6 +13,10 @@ public LadderFactory(LineFactory lineFactory) { this.lineFactory = lineFactory; } + public Ladder create(Participants participants, int height) { + return create(participants.size(), height); + } + public Ladder create(int numberOfLine, int height) { validateHeight(height); return new Ladder(lines(numberOfLine, height)); diff --git a/src/main/java/nextstep/ladder/domain/Participants.java b/src/main/java/nextstep/ladder/domain/Participants.java index 4574768208..cf97863518 100644 --- a/src/main/java/nextstep/ladder/domain/Participants.java +++ b/src/main/java/nextstep/ladder/domain/Participants.java @@ -21,6 +21,10 @@ public Participants(List names) { this.names = names; } + public int size() { + return names.size(); + } + private static List participantsNames(String[] names) { return Arrays.stream(names) .map(String::trim) diff --git a/src/main/java/nextstep/ladder/presentation/PromptMessage.java b/src/main/java/nextstep/ladder/presentation/PromptMessage.java new file mode 100644 index 0000000000..9d7a6b94dc --- /dev/null +++ b/src/main/java/nextstep/ladder/presentation/PromptMessage.java @@ -0,0 +1,17 @@ +package nextstep.ladder.presentation; + +public enum PromptMessage { + PARTICIPANTS_NAMES_QUESTION_MESSAGE("참여할 사람 이름을 입력하세요. (이름은 쉼표(,)로 구분하세요)"), + MAXIMUM_LADDER_HEIGHT_QUESTION_MESSAGE("최대 사다리 높이는 몇 개인가요?"), + EXECUTE_RESULT("실행결과"); + + private final String message; + + PromptMessage(String message) { + this.message = message; + } + + public String message() { + return message; + } +} diff --git a/src/main/java/nextstep/ladder/presentation/util/ConsoleInputUtil.java b/src/main/java/nextstep/ladder/presentation/util/ConsoleInputUtil.java new file mode 100644 index 0000000000..0f98d4eb39 --- /dev/null +++ b/src/main/java/nextstep/ladder/presentation/util/ConsoleInputUtil.java @@ -0,0 +1,22 @@ +package nextstep.ladder.presentation.util; + +import java.util.Scanner; + +import static nextstep.ladder.presentation.PromptMessage.*; + +public class ConsoleInputUtil { + private static final Scanner SCANNER = new Scanner(System.in); + + private ConsoleInputUtil() { + } + + public static String askParticipantNames() { + System.out.println(PARTICIPANTS_NAMES_QUESTION_MESSAGE.message()); + return SCANNER.nextLine(); + } + + public static int askMaximumLadderHeight() { + System.out.println(MAXIMUM_LADDER_HEIGHT_QUESTION_MESSAGE.message()); + return SCANNER.nextInt(); + } +} From 7f4ac45d72434c59dbf3ec0c1a7d673eae9aa1cc Mon Sep 17 00:00:00 2001 From: seriouskang Date: Sun, 14 May 2023 13:44:09 +0900 Subject: [PATCH 09/17] =?UTF-8?q?feat(ConnectionResultView):=20=EC=97=B0?= =?UTF-8?q?=EA=B2=B0=EC=84=A0=20=EC=B6=9C=EB=A0=A5=EC=9D=84=20=EC=9C=84?= =?UTF-8?q?=ED=95=9C=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=8B=A0=EA=B7=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/ConnectionResultView.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 src/main/java/nextstep/ladder/presentation/ConnectionResultView.java diff --git a/src/main/java/nextstep/ladder/presentation/ConnectionResultView.java b/src/main/java/nextstep/ladder/presentation/ConnectionResultView.java new file mode 100644 index 0000000000..bb02d4873c --- /dev/null +++ b/src/main/java/nextstep/ladder/presentation/ConnectionResultView.java @@ -0,0 +1,24 @@ +package nextstep.ladder.presentation; + +public class ConnectionResultView { + private static final String EMPTY_SPACE = " "; + private static final String DASH = "-"; + private static final int CONNECTION_PRINT_LENGTH = 5; + private final boolean connection; + + public ConnectionResultView(boolean connection) { + this.connection = connection; + } + + public String connection() { + return connectionMark().repeat(CONNECTION_PRINT_LENGTH); + } + + private String connectionMark() { + if(connection) { + return DASH; + } + + return EMPTY_SPACE; + } +} From b3c5cba4d1b9915b302c50281430ee3c3dfe5dad Mon Sep 17 00:00:00 2001 From: seriouskang Date: Sun, 14 May 2023 13:58:57 +0900 Subject: [PATCH 10/17] =?UTF-8?q?feat(LineResultView):=20=EC=82=AC?= =?UTF-8?q?=EB=8B=A4=EB=A6=AC=EC=9D=98=20=ED=95=9C=20=EB=9D=BC=EC=9D=B8?= =?UTF-8?q?=EC=9D=84=20=EC=B6=9C=EB=A0=A5=ED=95=98=EA=B8=B0=20=EC=9C=84?= =?UTF-8?q?=ED=95=9C=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=8B=A0=EA=B7=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nextstep/ladder/domain/Connections.java | 5 +++ .../java/nextstep/ladder/domain/Line.java | 4 +++ .../ladder/presentation/LineResultView.java | 31 +++++++++++++++++++ 3 files changed, 40 insertions(+) create mode 100644 src/main/java/nextstep/ladder/presentation/LineResultView.java diff --git a/src/main/java/nextstep/ladder/domain/Connections.java b/src/main/java/nextstep/ladder/domain/Connections.java index ee5cdeae55..abb34a3b04 100644 --- a/src/main/java/nextstep/ladder/domain/Connections.java +++ b/src/main/java/nextstep/ladder/domain/Connections.java @@ -1,5 +1,6 @@ package nextstep.ladder.domain; +import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.stream.IntStream; @@ -14,6 +15,10 @@ public Connections(List connections) { this.connections = connections; } + public List connections() { + return Collections.unmodifiableList(connections); + } + private void validateConnections(List connections) { if(connections == null) { throw new IllegalArgumentException("가로 라인의 연결선은 null이 될 수 없습니다."); diff --git a/src/main/java/nextstep/ladder/domain/Line.java b/src/main/java/nextstep/ladder/domain/Line.java index 128dc40912..3576d9866f 100644 --- a/src/main/java/nextstep/ladder/domain/Line.java +++ b/src/main/java/nextstep/ladder/domain/Line.java @@ -19,6 +19,10 @@ public Line(Connections connections) { this.connections = connections; } + public List connections() { + return connections.connections(); + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/src/main/java/nextstep/ladder/presentation/LineResultView.java b/src/main/java/nextstep/ladder/presentation/LineResultView.java new file mode 100644 index 0000000000..605d25e5e5 --- /dev/null +++ b/src/main/java/nextstep/ladder/presentation/LineResultView.java @@ -0,0 +1,31 @@ +package nextstep.ladder.presentation; + +import nextstep.ladder.domain.Line; + +public class LineResultView { + private static final String INITIAL_LINE_PRINT_FORMAT = "%6s"; + private static final String VERTICAL_BAR = "|"; + private final Line line; + + public LineResultView(Line line) { + this.line = line; + } + + public void printLine() { + System.out.println(line()); + } + + private StringBuilder line() { + StringBuilder printLine = initialLine(); + line.connections().stream() + .map(ConnectionResultView::new) + .forEach(connectionResultView -> printLine.append(connectionResultView.connection()).append(VERTICAL_BAR)); + + return printLine; + } + + private StringBuilder initialLine() { + return new StringBuilder() + .append(String.format(INITIAL_LINE_PRINT_FORMAT, VERTICAL_BAR)); + } +} From 9621f60e7e2176ed4a57363feb8d2a37705cd3fd Mon Sep 17 00:00:00 2001 From: seriouskang Date: Sun, 14 May 2023 14:00:13 +0900 Subject: [PATCH 11/17] =?UTF-8?q?feat(LadderResultView);=20=EC=82=AC?= =?UTF-8?q?=EB=8B=A4=EB=A6=AC=20=EC=B6=9C=EB=A0=A5=EC=9D=84=20=EC=9C=84?= =?UTF-8?q?=ED=95=9C=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=8B=A0=EA=B7=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ladder/presentation/LadderResultView.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 src/main/java/nextstep/ladder/presentation/LadderResultView.java diff --git a/src/main/java/nextstep/ladder/presentation/LadderResultView.java b/src/main/java/nextstep/ladder/presentation/LadderResultView.java new file mode 100644 index 0000000000..a816c40574 --- /dev/null +++ b/src/main/java/nextstep/ladder/presentation/LadderResultView.java @@ -0,0 +1,17 @@ +package nextstep.ladder.presentation; + +import nextstep.ladder.domain.Ladder; + +public class LadderResultView { + private final Ladder ladder; + + public LadderResultView(Ladder ladder) { + this.ladder = ladder; + } + + public void printLadder() { + ladder.lines().stream() + .map(LineResultView::new) + .forEach(LineResultView::printLine); + } +} From 98c638ab7bf33cf6191442ef228add1ccd9b84fc Mon Sep 17 00:00:00 2001 From: seriouskang Date: Sun, 14 May 2023 14:09:43 +0900 Subject: [PATCH 12/17] =?UTF-8?q?feat(ParticipantResultView):=20=EC=B0=B8?= =?UTF-8?q?=EA=B0=80=EC=9E=90=20=EC=B6=9C=EB=A0=A5=EC=9D=84=20=EC=9C=84?= =?UTF-8?q?=ED=95=9C=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=8B=A0=EA=B7=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/nextstep/ladder/domain/Ladder.java | 5 ++++ .../ladder/domain/ParticipantName.java | 4 +++ .../nextstep/ladder/domain/Participants.java | 6 +++++ .../ladder/presentation/LineResultView.java | 12 +++------ .../presentation/ParticipantResultView.java | 25 +++++++++++++++++++ 5 files changed, 44 insertions(+), 8 deletions(-) create mode 100644 src/main/java/nextstep/ladder/presentation/ParticipantResultView.java diff --git a/src/main/java/nextstep/ladder/domain/Ladder.java b/src/main/java/nextstep/ladder/domain/Ladder.java index c0e2c61323..fb269fcccc 100644 --- a/src/main/java/nextstep/ladder/domain/Ladder.java +++ b/src/main/java/nextstep/ladder/domain/Ladder.java @@ -1,6 +1,7 @@ package nextstep.ladder.domain; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Objects; @@ -15,6 +16,10 @@ public Ladder(List lines) { this.lines = lines; } + public List lines() { + return Collections.unmodifiableList(lines); + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/src/main/java/nextstep/ladder/domain/ParticipantName.java b/src/main/java/nextstep/ladder/domain/ParticipantName.java index b7b1d8aa5c..1709a62d1d 100644 --- a/src/main/java/nextstep/ladder/domain/ParticipantName.java +++ b/src/main/java/nextstep/ladder/domain/ParticipantName.java @@ -11,6 +11,10 @@ public ParticipantName(String name) { this.name = name; } + public String name() { + return name; + } + private void validateName(String name) { if(name == null || name.isBlank()) { throw new IllegalArgumentException("이름은 null이거나 공백일 수 없습니다: " + name); diff --git a/src/main/java/nextstep/ladder/domain/Participants.java b/src/main/java/nextstep/ladder/domain/Participants.java index cf97863518..fc1c009951 100644 --- a/src/main/java/nextstep/ladder/domain/Participants.java +++ b/src/main/java/nextstep/ladder/domain/Participants.java @@ -25,6 +25,12 @@ public int size() { return names.size(); } + public List names() { + return names.stream() + .map(ParticipantName::name) + .collect(Collectors.toUnmodifiableList()); + } + private static List participantsNames(String[] names) { return Arrays.stream(names) .map(String::trim) diff --git a/src/main/java/nextstep/ladder/presentation/LineResultView.java b/src/main/java/nextstep/ladder/presentation/LineResultView.java index 605d25e5e5..e6db404bc0 100644 --- a/src/main/java/nextstep/ladder/presentation/LineResultView.java +++ b/src/main/java/nextstep/ladder/presentation/LineResultView.java @@ -12,19 +12,15 @@ public LineResultView(Line line) { } public void printLine() { - System.out.println(line()); - } - - private StringBuilder line() { - StringBuilder printLine = initialLine(); + StringBuilder printFormatOfLine = initialPrintFormatOfLine(); line.connections().stream() .map(ConnectionResultView::new) - .forEach(connectionResultView -> printLine.append(connectionResultView.connection()).append(VERTICAL_BAR)); + .forEach(connectionResultView -> printFormatOfLine.append(connectionResultView.connection()).append(VERTICAL_BAR)); - return printLine; + System.out.println(printFormatOfLine); } - private StringBuilder initialLine() { + private StringBuilder initialPrintFormatOfLine() { return new StringBuilder() .append(String.format(INITIAL_LINE_PRINT_FORMAT, VERTICAL_BAR)); } diff --git a/src/main/java/nextstep/ladder/presentation/ParticipantResultView.java b/src/main/java/nextstep/ladder/presentation/ParticipantResultView.java new file mode 100644 index 0000000000..86012680c5 --- /dev/null +++ b/src/main/java/nextstep/ladder/presentation/ParticipantResultView.java @@ -0,0 +1,25 @@ +package nextstep.ladder.presentation; + +import nextstep.ladder.domain.Participants; + +public class ParticipantResultView { + private static final String PARTICIPANT_NAME_PRINT_FORMAT = "%6s"; + + private final Participants participants; + + public ParticipantResultView(Participants participants) { + this.participants = participants; + } + + public void printNames() { + StringBuilder participantsName = new StringBuilder(); + participants.names() + .forEach(participantName -> participantsName.append(printFormatOfParticipantName(participantName))); + + System.out.println(participantsName); + } + + private String printFormatOfParticipantName(String participantName) { + return String.format(PARTICIPANT_NAME_PRINT_FORMAT, participantName); + } +} From e04869ad357dae4dccde96728d7a7eac0d0ef7d9 Mon Sep 17 00:00:00 2001 From: seriouskang Date: Sun, 14 May 2023 14:12:48 +0900 Subject: [PATCH 13/17] =?UTF-8?q?feat(LadderGameResultView):=20=EC=82=AC?= =?UTF-8?q?=EB=8B=A4=EB=A6=AC=20=EA=B2=8C=EC=9E=84=20=EA=B2=B0=EA=B3=BC=20?= =?UTF-8?q?=EC=B6=9C=EB=A0=A5=EC=9D=84=20=EC=9C=84=ED=95=9C=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=20=EC=8B=A0=EA=B7=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/LadderGameResultView.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 src/main/java/nextstep/ladder/presentation/LadderGameResultView.java diff --git a/src/main/java/nextstep/ladder/presentation/LadderGameResultView.java b/src/main/java/nextstep/ladder/presentation/LadderGameResultView.java new file mode 100644 index 0000000000..158abad8f0 --- /dev/null +++ b/src/main/java/nextstep/ladder/presentation/LadderGameResultView.java @@ -0,0 +1,22 @@ +package nextstep.ladder.presentation; + +import nextstep.ladder.domain.Ladder; +import nextstep.ladder.domain.Participants; + +import static nextstep.ladder.presentation.PromptMessage.*; + +public class LadderGameResultView { + private final ParticipantResultView participantResultView; + private final LadderResultView ladderResultView; + + public LadderGameResultView(Participants participants, Ladder ladder) { + participantResultView = new ParticipantResultView(participants); + ladderResultView = new LadderResultView(ladder); + } + + public void printExecuteResult() { + System.out.println(EXECUTE_RESULT.message()); + participantResultView.printNames(); + ladderResultView.printLadder(); + } +} From c407e711614f7473b24729eabbed56330a18f3a9 Mon Sep 17 00:00:00 2001 From: seriouskang Date: Sun, 14 May 2023 14:21:21 +0900 Subject: [PATCH 14/17] =?UTF-8?q?feat(RandomConnectionsFactory):=20?= =?UTF-8?q?=EB=9E=9C=EB=8D=A4=ED=95=9C=20=EC=97=B0=EA=B2=B0=EC=84=A0=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=EC=9D=84=20=EC=9C=84=ED=95=9C=20=ED=8C=A9?= =?UTF-8?q?=ED=86=A0=EB=A6=AC=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=8B=A0?= =?UTF-8?q?=EA=B7=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/RandomConnectionsFactory.java | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 src/main/java/nextstep/ladder/domain/RandomConnectionsFactory.java diff --git a/src/main/java/nextstep/ladder/domain/RandomConnectionsFactory.java b/src/main/java/nextstep/ladder/domain/RandomConnectionsFactory.java new file mode 100644 index 0000000000..7087bebbd6 --- /dev/null +++ b/src/main/java/nextstep/ladder/domain/RandomConnectionsFactory.java @@ -0,0 +1,47 @@ +package nextstep.ladder.domain; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; +import java.util.stream.IntStream; + +public class RandomConnectionsFactory implements ConnectionsFactory { + private static final int ZERO = 0; + private static final int ONE = 1; + + @Override + public Connections create(int numberOfConnections) { + return new Connections(createConnections(numberOfConnections)); + } + + private List createConnections(int numberOfConnections) { + if(numberOfConnections == ZERO) { + return Collections.emptyList(); + } + + List connections = new ArrayList<>(); + connections.add(randomBoolean()); + + IntStream.range(ONE, numberOfConnections) + .mapToObj(index -> createConnection(previousElementOf(connections, index))) + .forEach(connections::add); + return connections; + } + + private Boolean createConnection(boolean previousElement) { + if(previousElement) { + return false; + } + + return randomBoolean(); + } + + private Boolean previousElementOf(List connections, int index) { + return connections.get(index - ONE); + } + + private Boolean randomBoolean() { + return ThreadLocalRandom.current().nextBoolean(); + } +} From 27e4a27af4e7a67479084f904a1ce7d458427f3f Mon Sep 17 00:00:00 2001 From: seriouskang Date: Sun, 14 May 2023 14:41:42 +0900 Subject: [PATCH 15/17] =?UTF-8?q?feat(Connection):=20=EC=97=B0=EA=B2=B0=20?= =?UTF-8?q?=EC=97=AC=EB=B6=80=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=8B=A0?= =?UTF-8?q?=EA=B7=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nextstep/ladder/domain/Connection.java | 28 +++++++++++++++++++ .../nextstep/ladder/domain/Connections.java | 16 +++++------ .../java/nextstep/ladder/domain/Line.java | 9 ++++-- .../domain/RandomConnectionsFactory.java | 22 +++++++-------- .../presentation/ConnectionResultView.java | 8 ++++-- .../ladder/domain/ConnectionsTest.java | 26 ++++++++--------- .../ladder/domain/LadderFactoryTest.java | 3 +- .../ladder/domain/LineFactoryTest.java | 3 +- 8 files changed, 75 insertions(+), 40 deletions(-) create mode 100644 src/main/java/nextstep/ladder/domain/Connection.java diff --git a/src/main/java/nextstep/ladder/domain/Connection.java b/src/main/java/nextstep/ladder/domain/Connection.java new file mode 100644 index 0000000000..0659978163 --- /dev/null +++ b/src/main/java/nextstep/ladder/domain/Connection.java @@ -0,0 +1,28 @@ +package nextstep.ladder.domain; + +import java.util.Objects; + +public class Connection { + private final boolean connection; + + public Connection(boolean connection) { + this.connection = connection; + } + + public boolean isConnected() { + return connection; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Connection)) return false; + Connection that = (Connection) o; + return connection == that.connection; + } + + @Override + public int hashCode() { + return Objects.hash(connection); + } +} diff --git a/src/main/java/nextstep/ladder/domain/Connections.java b/src/main/java/nextstep/ladder/domain/Connections.java index abb34a3b04..98def7c7fd 100644 --- a/src/main/java/nextstep/ladder/domain/Connections.java +++ b/src/main/java/nextstep/ladder/domain/Connections.java @@ -8,18 +8,18 @@ public class Connections { private static final int START_INDEX_OF_CONNECTIONS = 0; private static final int ONE = 1; - private final List connections; + private final List connections; - public Connections(List connections) { + public Connections(List connections) { validateConnections(connections); this.connections = connections; } - public List connections() { + public List connections() { return Collections.unmodifiableList(connections); } - private void validateConnections(List connections) { + private void validateConnections(List connections) { if(connections == null) { throw new IllegalArgumentException("가로 라인의 연결선은 null이 될 수 없습니다."); } @@ -29,17 +29,17 @@ private void validateConnections(List connections) { } } - private boolean containsConsecutiveConnections(List connections) { + private boolean containsConsecutiveConnections(List connections) { return IntStream.range(START_INDEX_OF_CONNECTIONS, beforeLastIndexOf(connections)) .anyMatch(index -> isConsecutiveConnections(connections, index)); } - private int beforeLastIndexOf(List connections) { + private int beforeLastIndexOf(List connections) { return connections.size() - ONE; } - private boolean isConsecutiveConnections(List connections, int index) { - return connections.get(index) && connections.get(index + ONE); + private boolean isConsecutiveConnections(List connections, int index) { + return connections.get(index).isConnected() && connections.get(index + ONE).isConnected(); } @Override diff --git a/src/main/java/nextstep/ladder/domain/Line.java b/src/main/java/nextstep/ladder/domain/Line.java index 3576d9866f..e338f80f4e 100644 --- a/src/main/java/nextstep/ladder/domain/Line.java +++ b/src/main/java/nextstep/ladder/domain/Line.java @@ -3,15 +3,18 @@ import java.util.Arrays; import java.util.List; import java.util.Objects; +import java.util.stream.Collectors; public class Line { private final Connections connections; public Line(Boolean...connections) { - this(Arrays.asList(connections)); + this(Arrays.stream(connections) + .map(Connection::new) + .collect(Collectors.toUnmodifiableList())); } - public Line(List connections) { + public Line(List connections) { this(new Connections(connections)); } @@ -19,7 +22,7 @@ public Line(Connections connections) { this.connections = connections; } - public List connections() { + public List connections() { return connections.connections(); } diff --git a/src/main/java/nextstep/ladder/domain/RandomConnectionsFactory.java b/src/main/java/nextstep/ladder/domain/RandomConnectionsFactory.java index 7087bebbd6..58e428447b 100644 --- a/src/main/java/nextstep/ladder/domain/RandomConnectionsFactory.java +++ b/src/main/java/nextstep/ladder/domain/RandomConnectionsFactory.java @@ -15,33 +15,33 @@ public Connections create(int numberOfConnections) { return new Connections(createConnections(numberOfConnections)); } - private List createConnections(int numberOfConnections) { + private List createConnections(int numberOfConnections) { if(numberOfConnections == ZERO) { return Collections.emptyList(); } - List connections = new ArrayList<>(); - connections.add(randomBoolean()); + List connections = new ArrayList<>(); + connections.add(randomConnection()); IntStream.range(ONE, numberOfConnections) - .mapToObj(index -> createConnection(previousElementOf(connections, index))) + .mapToObj(index -> createConnection(previousConnection(connections, index))) .forEach(connections::add); return connections; } - private Boolean createConnection(boolean previousElement) { - if(previousElement) { - return false; + private Connection createConnection(Connection previousConnection) { + if(previousConnection.isConnected()) { + return new Connection(false); } - return randomBoolean(); + return randomConnection(); } - private Boolean previousElementOf(List connections, int index) { + private Connection previousConnection(List connections, int index) { return connections.get(index - ONE); } - private Boolean randomBoolean() { - return ThreadLocalRandom.current().nextBoolean(); + private Connection randomConnection() { + return new Connection(ThreadLocalRandom.current().nextBoolean()); } } diff --git a/src/main/java/nextstep/ladder/presentation/ConnectionResultView.java b/src/main/java/nextstep/ladder/presentation/ConnectionResultView.java index bb02d4873c..3b376a3647 100644 --- a/src/main/java/nextstep/ladder/presentation/ConnectionResultView.java +++ b/src/main/java/nextstep/ladder/presentation/ConnectionResultView.java @@ -1,12 +1,14 @@ package nextstep.ladder.presentation; +import nextstep.ladder.domain.Connection; + public class ConnectionResultView { private static final String EMPTY_SPACE = " "; private static final String DASH = "-"; private static final int CONNECTION_PRINT_LENGTH = 5; - private final boolean connection; + private final Connection connection; - public ConnectionResultView(boolean connection) { + public ConnectionResultView(Connection connection) { this.connection = connection; } @@ -15,7 +17,7 @@ public String connection() { } private String connectionMark() { - if(connection) { + if(connection.isConnected()) { return DASH; } diff --git a/src/test/java/nextstep/ladder/domain/ConnectionsTest.java b/src/test/java/nextstep/ladder/domain/ConnectionsTest.java index 9743a671cc..511aee6b22 100644 --- a/src/test/java/nextstep/ladder/domain/ConnectionsTest.java +++ b/src/test/java/nextstep/ladder/domain/ConnectionsTest.java @@ -15,7 +15,7 @@ class ConnectionsTest { @ParameterizedTest(name = "[{index}/3] {displayName}") @MethodSource("consecutiveConnections") @DisplayName("가로 라인의 연결선이 연속해서 존재할 경우, IllegalArgumentException 예외 발생") - void consecutive_connections_then_throw_IllegalArgumentException(List consecutiveConnections) { + void consecutive_connections_then_throw_IllegalArgumentException(List consecutiveConnections) { assertThatThrownBy(() -> new Connections(consecutiveConnections)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("가로 라인의 연결선은 연속해서 존재할 수 없습니다."); @@ -24,7 +24,7 @@ void consecutive_connections_then_throw_IllegalArgumentException(List c @ParameterizedTest(name = "[{index}/9] {displayName}") @MethodSource("validConnections") @DisplayName("가로 라인 연견설이 연속해서 존재하지 않을 경우, Connections 객체 생성") - void valid_connections(List validConnections) { + void valid_connections(List validConnections) { assertThat(new Connections(validConnections)) .isInstanceOf(Connections.class); } @@ -39,23 +39,23 @@ void null_connections_then_throw_IllegalArgumentException() { static Stream consecutiveConnections() { return Stream.of( - Arguments.arguments(List.of(true, true)), - Arguments.arguments(List.of(false, true, true)), - Arguments.arguments(List.of(true, true, false)) + Arguments.arguments(List.of(new Connection(true), new Connection(true))), + Arguments.arguments(List.of(new Connection(false), new Connection(true), new Connection(true))), + Arguments.arguments(List.of(new Connection(true), new Connection(true), new Connection(false))) ); } static Stream validConnections() { return Stream.of( Arguments.arguments(List.of()), - Arguments.arguments(List.of(false)), - Arguments.arguments(List.of(true)), - Arguments.arguments(List.of(true, false)), - Arguments.arguments(List.of(false, true)), - Arguments.arguments(List.of(true, false, false)), - Arguments.arguments(List.of(false, true, false)), - Arguments.arguments(List.of(false, false, true)), - Arguments.arguments(List.of(true, false, true)) + Arguments.arguments(List.of(new Connection(false))), + Arguments.arguments(List.of(new Connection(true))), + Arguments.arguments(List.of(new Connection(true), new Connection(false))), + Arguments.arguments(List.of(new Connection(false), new Connection(true))), + Arguments.arguments(List.of(new Connection(true), new Connection(false), new Connection(false))), + Arguments.arguments(List.of(new Connection(false), new Connection(true), new Connection(false))), + Arguments.arguments(List.of(new Connection(false), new Connection(false), new Connection(true))), + Arguments.arguments(List.of(new Connection(true), new Connection(false), new Connection(true))) ); } } \ No newline at end of file diff --git a/src/test/java/nextstep/ladder/domain/LadderFactoryTest.java b/src/test/java/nextstep/ladder/domain/LadderFactoryTest.java index 017f6a107b..89f5c32fbb 100644 --- a/src/test/java/nextstep/ladder/domain/LadderFactoryTest.java +++ b/src/test/java/nextstep/ladder/domain/LadderFactoryTest.java @@ -50,9 +50,10 @@ private ConnectionsFactory evenConnectedConnectionsFactory() { return numberOfConnections -> new Connections(evenElementTrueList(numberOfConnections)); } - private List evenElementTrueList(int size) { + private List evenElementTrueList(int size) { return IntStream.range(0, size) .mapToObj(index -> index % 2 == 0) + .map(Connection::new) .collect(Collectors.toUnmodifiableList()); } } \ No newline at end of file diff --git a/src/test/java/nextstep/ladder/domain/LineFactoryTest.java b/src/test/java/nextstep/ladder/domain/LineFactoryTest.java index b3b149353d..c2c108c9c6 100644 --- a/src/test/java/nextstep/ladder/domain/LineFactoryTest.java +++ b/src/test/java/nextstep/ladder/domain/LineFactoryTest.java @@ -40,9 +40,10 @@ private ConnectionsFactory evenConnectedConnectionsFactory() { return numberOfConnections -> new Connections(evenElementTrueList(numberOfConnections)); } - private List evenElementTrueList(int size) { + private List evenElementTrueList(int size) { return IntStream.range(0, size) .mapToObj(index -> index % 2 == 0) + .map(Connection::new) .collect(Collectors.toUnmodifiableList()); } } \ No newline at end of file From d96a63583ce1c875b399190bd097c6e0fea96ed9 Mon Sep 17 00:00:00 2001 From: seriouskang Date: Sun, 14 May 2023 14:45:25 +0900 Subject: [PATCH 16/17] =?UTF-8?q?feat(LadderApplication):=20=EC=82=AC?= =?UTF-8?q?=EB=8B=A4=EB=A6=AC=20=EA=B2=8C=EC=9E=84=20=EB=A9=94=EC=9D=B8=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=8B=A0=EA=B7=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nextstep/ladder/LadderApplication.java | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 src/main/java/nextstep/ladder/LadderApplication.java diff --git a/src/main/java/nextstep/ladder/LadderApplication.java b/src/main/java/nextstep/ladder/LadderApplication.java new file mode 100644 index 0000000000..6f2302da5b --- /dev/null +++ b/src/main/java/nextstep/ladder/LadderApplication.java @@ -0,0 +1,35 @@ +package nextstep.ladder; + +import nextstep.ladder.domain.*; +import nextstep.ladder.presentation.LadderGameResultView; +import nextstep.ladder.presentation.util.ConsoleInputUtil; + +public class LadderApplication { + public static void main(String[] args) { + Participants participants = new Participants(participantNames()); + Ladder ladder = ladderFactory().create(participants, maximumLadderHeight()); + + LadderGameResultView ladderGameResultView = new LadderGameResultView(participants, ladder); + ladderGameResultView.printExecuteResult(); + } + + private static LadderFactory ladderFactory() { + return new LadderFactory(lineFactory()); + } + + private static LineFactory lineFactory() { + return new LineFactory(randomConnectionsFactory()); + } + + private static ConnectionsFactory randomConnectionsFactory() { + return new RandomConnectionsFactory(); + } + + private static int maximumLadderHeight() { + return ConsoleInputUtil.askMaximumLadderHeight(); + } + + private static String participantNames() { + return ConsoleInputUtil.askParticipantNames(); + } +} From a485e1e124d05a74c6f720c14429ad66447a23c5 Mon Sep 17 00:00:00 2001 From: seriouskang Date: Sun, 14 May 2023 14:47:06 +0900 Subject: [PATCH 17/17] =?UTF-8?q?docs(README.md):=20=ED=94=84=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=EB=9E=98=EB=B0=8D=20=EB=B0=8F=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EC=9A=94=EA=B5=AC=20=EC=82=AC=ED=95=AD=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f633c087b0..aa88b19a03 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ * [텍스트와 이미지로 살펴보는 온라인 코드 리뷰 과정](https://github.com/nextstep-step/nextstep-docs/tree/master/codereview) -## step1(1단계) - 스트림, 람다, Optional +## step1 - 스트림, 람다, Optional ### 람다 실습 - [x] 익명 클래스를 람다로 전환 > `CarTest` @@ -31,3 +31,19 @@ > 메소드 인자로 받은 `User`를 `Optional`로 생성하여 메서드 구현 - [x] `Users` 클래스의 `getUser()` 메서드를 `stream`과 `Optional`을 활용해 리팩토링 - [x] `Expression`의 `of` 메서드를 `stream` 기반으로 리팩토링 + + +## step2 - 사다리(생성) +### 프로그래밍 요구 사항 +- 자바 8의 스트림과 람다를 적용해 프로그래밍 +- 규칙 6: 모든 엔티티를 작게 유지 + +### 기능 요구 사항 +- [x] 사다리 게임에 참여하는 사람의 이름을 최대 5글자까지 부여 + > 참여 최소 인원은 1명 +- [x] 사람 이름은 쉼표(`,`)를 기준으로 구분 +- [x] 사다리 출력 시, 사람 이름을 5자 기준으로 함께 출력 +- [x] 사다리 타기가 정상적으로 동작하려면 라인이 겹치지 않도록 해야 한다. + > 가로 라인이 겹치는 경우(ex. `|-----|-----|`) 어느 방향으로 이동할지 결정할 수 없다. + > 최소 사다리 높이는 1 + > 최소 라인 연결수는 0개 \ No newline at end of file