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 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(); + } +} 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 new file mode 100644 index 0000000000..98def7c7fd --- /dev/null +++ b/src/main/java/nextstep/ladder/domain/Connections.java @@ -0,0 +1,57 @@ +package nextstep.ladder.domain; + +import java.util.Collections; +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) { + validateConnections(connections); + this.connections = connections; + } + + public List connections() { + return Collections.unmodifiableList(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() - ONE; + } + + private boolean isConsecutiveConnections(List connections, int index) { + return connections.get(index).isConnected() && connections.get(index + ONE).isConnected(); + } + + @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/Ladder.java b/src/main/java/nextstep/ladder/domain/Ladder.java new file mode 100644 index 0000000000..fb269fcccc --- /dev/null +++ b/src/main/java/nextstep/ladder/domain/Ladder.java @@ -0,0 +1,35 @@ +package nextstep.ladder.domain; + +import java.util.Arrays; +import java.util.Collections; +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; + } + + public List lines() { + return Collections.unmodifiableList(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..3b94ca5991 --- /dev/null +++ b/src/main/java/nextstep/ladder/domain/LadderFactory.java @@ -0,0 +1,40 @@ +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(Participants participants, int height) { + return create(participants.size(), height); + } + + 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/main/java/nextstep/ladder/domain/Line.java b/src/main/java/nextstep/ladder/domain/Line.java new file mode 100644 index 0000000000..e338f80f4e --- /dev/null +++ b/src/main/java/nextstep/ladder/domain/Line.java @@ -0,0 +1,41 @@ +package nextstep.ladder.domain; + +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.stream(connections) + .map(Connection::new) + .collect(Collectors.toUnmodifiableList())); + } + + public Line(List connections) { + this(new Connections(connections)); + } + + public Line(Connections connections) { + this.connections = connections; + } + + public List connections() { + return 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/main/java/nextstep/ladder/domain/ParticipantName.java b/src/main/java/nextstep/ladder/domain/ParticipantName.java new file mode 100644 index 0000000000..1709a62d1d --- /dev/null +++ b/src/main/java/nextstep/ladder/domain/ParticipantName.java @@ -0,0 +1,40 @@ +package nextstep.ladder.domain; + +import java.util.Objects; + +public class ParticipantName { + private static final int MAX_NAME_LENGTH = 5; + private final String name; + + public ParticipantName(String name) { + validateName(name); + this.name = name; + } + + public String name() { + return 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); + } + } + + @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..fc1c009951 --- /dev/null +++ b/src/main/java/nextstep/ladder/domain/Participants.java @@ -0,0 +1,53 @@ +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; + } + + 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) + .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/main/java/nextstep/ladder/domain/RandomConnectionsFactory.java b/src/main/java/nextstep/ladder/domain/RandomConnectionsFactory.java new file mode 100644 index 0000000000..58e428447b --- /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(randomConnection()); + + IntStream.range(ONE, numberOfConnections) + .mapToObj(index -> createConnection(previousConnection(connections, index))) + .forEach(connections::add); + return connections; + } + + private Connection createConnection(Connection previousConnection) { + if(previousConnection.isConnected()) { + return new Connection(false); + } + + return randomConnection(); + } + + private Connection previousConnection(List connections, int index) { + return connections.get(index - ONE); + } + + 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 new file mode 100644 index 0000000000..3b376a3647 --- /dev/null +++ b/src/main/java/nextstep/ladder/presentation/ConnectionResultView.java @@ -0,0 +1,26 @@ +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 Connection connection; + + public ConnectionResultView(Connection connection) { + this.connection = connection; + } + + public String connection() { + return connectionMark().repeat(CONNECTION_PRINT_LENGTH); + } + + private String connectionMark() { + if(connection.isConnected()) { + return DASH; + } + + return EMPTY_SPACE; + } +} 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(); + } +} 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); + } +} 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..e6db404bc0 --- /dev/null +++ b/src/main/java/nextstep/ladder/presentation/LineResultView.java @@ -0,0 +1,27 @@ +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() { + StringBuilder printFormatOfLine = initialPrintFormatOfLine(); + line.connections().stream() + .map(ConnectionResultView::new) + .forEach(connectionResultView -> printFormatOfLine.append(connectionResultView.connection()).append(VERTICAL_BAR)); + + System.out.println(printFormatOfLine); + } + + 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); + } +} 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(); + } +} diff --git a/src/test/java/nextstep/ladder/domain/ConnectionsTest.java b/src/test/java/nextstep/ladder/domain/ConnectionsTest.java new file mode 100644 index 0000000000..511aee6b22 --- /dev/null +++ b/src/test/java/nextstep/ladder/domain/ConnectionsTest.java @@ -0,0 +1,61 @@ +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 ConnectionsTest { + @ParameterizedTest(name = "[{index}/3] {displayName}") + @MethodSource("consecutiveConnections") + @DisplayName("가로 라인의 연결선이 연속해서 존재할 경우, IllegalArgumentException 예외 발생") + void consecutive_connections_then_throw_IllegalArgumentException(List consecutiveConnections) { + assertThatThrownBy(() -> new Connections(consecutiveConnections)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("가로 라인의 연결선은 연속해서 존재할 수 없습니다."); + } + + @ParameterizedTest(name = "[{index}/9] {displayName}") + @MethodSource("validConnections") + @DisplayName("가로 라인 연견설이 연속해서 존재하지 않을 경우, Connections 객체 생성") + void valid_connections(List validConnections) { + assertThat(new Connections(validConnections)) + .isInstanceOf(Connections.class); + } + + @Test + @DisplayName("가로 라인의 연결선이 null일 경우, IllegalArgumentException 예외 발생") + void null_connections_then_throw_IllegalArgumentException() { + assertThatThrownBy(() -> new Connections(null)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("가로 라인의 연결선은 null이 될 수 없습니다."); + } + + static Stream consecutiveConnections() { + return Stream.of( + 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(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 new file mode 100644 index 0000000000..89f5c32fbb --- /dev/null +++ b/src/test/java/nextstep/ladder/domain/LadderFactoryTest.java @@ -0,0 +1,59 @@ +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) + .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 new file mode 100644 index 0000000000..c2c108c9c6 --- /dev/null +++ b/src/test/java/nextstep/ladder/domain/LineFactoryTest.java @@ -0,0 +1,49 @@ +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) + .map(Connection::new) + .collect(Collectors.toUnmodifiableList()); + } +} \ No newline at end of file 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..d4b3051ee9 --- /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); + } +} \ 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