From a9fc3eaf8b3dee609edc55d0137feb139f6b180e Mon Sep 17 00:00:00 2001 From: yoondgu Date: Wed, 14 Dec 2022 19:28:58 +0900 Subject: [PATCH 01/26] =?UTF-8?q?chore:=20=EC=9E=90=EB=B0=94=20=EB=B2=84?= =?UTF-8?q?=EC=A0=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 45d61697f..edc026cd8 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ dependencies { java { toolchain { - languageVersion = JavaLanguageVersion.of(8) + languageVersion = JavaLanguageVersion.of(11) } } From c5f7c13b8a157c3721dbe01f2654b9d4c50dfaa1 Mon Sep 17 00:00:00 2001 From: yoondgu Date: Wed, 14 Dec 2022 19:29:44 +0900 Subject: [PATCH 02/26] =?UTF-8?q?style:=20=EC=BD=94=EB=93=9C=20=ED=8F=AC?= =?UTF-8?q?=EB=A7=B7=20=ED=86=B5=EC=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/racingcar/ApplicationTest.java | 24 ++++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/test/java/racingcar/ApplicationTest.java b/src/test/java/racingcar/ApplicationTest.java index 8f6f70a21..6332501aa 100644 --- a/src/test/java/racingcar/ApplicationTest.java +++ b/src/test/java/racingcar/ApplicationTest.java @@ -1,12 +1,12 @@ package racingcar; -import camp.nextstep.edu.missionutils.test.NsTest; -import org.junit.jupiter.api.Test; - import static camp.nextstep.edu.missionutils.test.Assertions.assertRandomNumberInRangeTest; import static camp.nextstep.edu.missionutils.test.Assertions.assertSimpleTest; import static org.assertj.core.api.Assertions.assertThat; +import camp.nextstep.edu.missionutils.test.NsTest; +import org.junit.jupiter.api.Test; + class ApplicationTest extends NsTest { private static final int MOVING_FORWARD = 4; private static final int STOP = 3; @@ -16,21 +16,21 @@ class ApplicationTest extends NsTest { @Test void 전진_정지() { assertRandomNumberInRangeTest( - () -> { - run("pobi,woni", "1"); - assertThat(output()).contains("pobi : -", "woni : ", "최종 우승자 : pobi"); - }, - MOVING_FORWARD, STOP + () -> { + run("pobi,woni", "1"); + assertThat(output()).contains("pobi : -", "woni : ", "최종 우승자 : pobi"); + }, + MOVING_FORWARD, STOP ); } @Test void 이름에_대한_예외_처리() { assertSimpleTest( - () -> { - runException("pobi,javaji"); - assertThat(output()).contains(ERROR_MESSAGE); - } + () -> { + runException("pobi,javaji"); + assertThat(output()).contains(ERROR_MESSAGE); + } ); } From 9cc2fc560efd368be9678a91e51e1ba089d574fe Mon Sep 17 00:00:00 2001 From: yoondgu Date: Wed, 14 Dec 2022 20:02:40 +0900 Subject: [PATCH 03/26] =?UTF-8?q?docs:=20=EB=AF=B8=EC=85=98=20=EB=AA=85?= =?UTF-8?q?=EC=84=B8=EC=84=9C=20=EA=B8=B0=EB=8A=A5=20=EB=AA=A9=EB=A1=9D=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/README.md | 104 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 docs/README.md diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 000000000..250e485a3 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,104 @@ +# [미션 - 자동차 경주 게임] 명세서 + +## 목차 + +1. [기능 목록](#-기능-목록) +2. [상수값 목록](#-상수값-목록) +3. [단위 테스트 목록](#-단위테스트-목록) +4. [클래스 설계](#-클래스-설계) +5. [리팩토링 검토 목록](#%EF%B8%8F-리팩토링-검토-목록) + +## 🚀 기능 목록 + +### 로직 분리에 따른 예외 발생 기준 + +- 예외 발생 시 적합한 표준예외를 사용한다. +- 기본 UI 검증 오류는 `view`에서, 도메인 기능에서 발생하는 오류는 `service`에서 예외를 발생시킨다. + +### 도메인 구성 요소 + +- 자동차 + - 이름 + - 위치 +- 게임에 참여한 모든 자동차 목록 +- 자동차가 이동하기 위한 값 생성기 +- 시도 회수 +- 게임 결과 + - 우승한 자동차 이름 모음 (1~n명) + +### 도메인 로직을 위한 기능 + +1. + - [ ] 경주할 모든 자동차의 이름을 저장하는 기능 + - [ ] `예외발생` : 이름의 길이가 5자 초과 + - [ ] `예외발생` : 경주할 자동차가 1대 +2. + - [ ] 랜덤 숫자를 생성하는 기능 +3. + - [ ] 자동차가 전달받은 숫자에 따라 이동(전진하거나 전진하지않음)하는 기능 + - [ ] `예외발생` : 전달받은 숫자가 범위 밖 +4. + - [ ] 자동차 간 위치를 비교해 가장 값이 큰, 최소 1명 최대 n명의 우승자를 구하는 기능 +5. + - [ ] 전달받은 회수 만큼 자동차 이동을 실행시키는 기능 + +### UI 로직을 위한 기능 + +1. + - [ ] 경주할 모든 자동차 이름을 형식에 맞게 입력받는 기능 + - [ ] `예외발생` : 빈 문자열 + - [ ] `예외발생` : 입력값 형식에 맞지 않음 +2. + - [ ] 시도할 회수를 입력받는 기능 + - [ ] `예외발생` : 자연수가 아닌 값 +3. + - [ ] 차수 별 실행 결과 출력 +4. + - [ ] 우승자 안내 문구 출력 +5. + - [ ] 이름 형식을 쉼표 구분 <-> 리스트로 파싱 +6. + - [ ] 에러 메시지 출력 +7. + - [ ] 잘못된 값 입력 시 그 부분부터 재입력 + +## 🗄 상수값 목록 + +### 도메인 로직 정보 + +- [ ] 이동 숫자 0 ~ 9, 전진 조건 4 +- [ ] 이름 길이 제한 5 + +### UI 로직 정보 + +- [ ] 구분자 쉼표 +- [ ] 입력 안내 메시지 +- [ ] 실행 결과 출력 형식 +- [ ] 우승자 안내 문구 출력 형식 +- [ ] 에러메시지 출력 형식 + +## ✅ 단위테스트 목록 + +### 도메인 로직 단위테스트 + +- [ ] 기능1 단위테스트 + - [ ] 실행 성공 + - [ ] 예외 테스트 + +## 🖋 클래스 설계 + +[작성 예정] + +## ♻️ 리팩토링 검토 목록 + +### 코드 스타일 및 요구사항 + +- [ ] 인덴트 2 이하 검토 +- [ ] 메소드 분리 검토 +- [ ] 메소드 길이 10라인 이하 검토 +- [ ] 파라미터 개수 3개 이하 검토 +- [ ] 변수, 메소드 선언 순서 정리 (메소드 정렬 기준 1순위: 메소드 간 논리적 연관성, 2순위: public-private) +- [ ] Java 코드 컨벤션 가이드 준수 검토 +- [ ] 요구사항에 주어진 클래스 적용 규칙 검토 +- [ ] TODO 주석 모두 해결 후 삭제 +- [ ] 클래스 주석 작성 \ No newline at end of file From 332470057c1378985e3b2d962d27a5150b00b01c Mon Sep 17 00:00:00 2001 From: yoondgu Date: Wed, 14 Dec 2022 20:33:29 +0900 Subject: [PATCH 04/26] =?UTF-8?q?feat:=20=EC=9D=B4=EB=A6=84=20=ED=98=95?= =?UTF-8?q?=EC=8B=9D=EC=9D=84=20=EC=89=BC=ED=91=9C=20=EA=B5=AC=EB=B6=84=20?= =?UTF-8?q?<->=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=EB=A1=9C=20=ED=8C=8C=EC=8B=B1?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/README.md | 14 +++---- .../view/constants/ErrorMessage.java | 6 +++ .../java/racingcar/view/constants/Format.java | 6 +++ .../racingcar/view/util/FormatParser.java | 38 +++++++++++++++++++ 4 files changed, 57 insertions(+), 7 deletions(-) create mode 100644 src/main/java/racingcar/view/constants/ErrorMessage.java create mode 100644 src/main/java/racingcar/view/constants/Format.java create mode 100644 src/main/java/racingcar/view/util/FormatParser.java diff --git a/docs/README.md b/docs/README.md index 250e485a3..ab46acd9e 100644 --- a/docs/README.md +++ b/docs/README.md @@ -46,21 +46,21 @@ 1. - [ ] 경주할 모든 자동차 이름을 형식에 맞게 입력받는 기능 - - [ ] `예외발생` : 빈 문자열 - - [ ] `예외발생` : 입력값 형식에 맞지 않음 + - [x] `예외발생` : 빈 문자열 + - [x] `예외발생` : 입력값 형식에 맞지 않음 2. - [ ] 시도할 회수를 입력받는 기능 - [ ] `예외발생` : 자연수가 아닌 값 3. - - [ ] 차수 별 실행 결과 출력 + - [ ] 차수 별 실행 결과 출력하는 기능 4. - - [ ] 우승자 안내 문구 출력 + - [ ] 우승자 안내 문구 출력하는 기능 5. - - [ ] 이름 형식을 쉼표 구분 <-> 리스트로 파싱 + - [x] 이름 형식을 쉼표 구분 <-> 리스트로 파싱하는 기능 6. - - [ ] 에러 메시지 출력 + - [ ] 에러 메시지 출력하는 기능 7. - - [ ] 잘못된 값 입력 시 그 부분부터 재입력 + - [ ] 잘못된 값 입력 시 그 부분부터 재입력하는 기능 ## 🗄 상수값 목록 diff --git a/src/main/java/racingcar/view/constants/ErrorMessage.java b/src/main/java/racingcar/view/constants/ErrorMessage.java new file mode 100644 index 000000000..a820082c3 --- /dev/null +++ b/src/main/java/racingcar/view/constants/ErrorMessage.java @@ -0,0 +1,6 @@ +package racingcar.view.constants; + +public class ErrorMessage { + public static final String FORMAT_EMPTY = "형식 내의 값 중 빈 문자열이 있습니다."; + public static final String INPUT_EMPTY = "입력값이 빈 값입니다."; +} diff --git a/src/main/java/racingcar/view/constants/Format.java b/src/main/java/racingcar/view/constants/Format.java new file mode 100644 index 000000000..d592389e8 --- /dev/null +++ b/src/main/java/racingcar/view/constants/Format.java @@ -0,0 +1,6 @@ +package racingcar.view.constants; + +public class Format { + public static final String INPUT_NAMES_DELIMITER = ","; + public static final String OUTPUT_NAMES_DELIMITER = ", "; +} diff --git a/src/main/java/racingcar/view/util/FormatParser.java b/src/main/java/racingcar/view/util/FormatParser.java new file mode 100644 index 000000000..4e8da4a7a --- /dev/null +++ b/src/main/java/racingcar/view/util/FormatParser.java @@ -0,0 +1,38 @@ +package racingcar.view.util; + +import java.util.Arrays; +import java.util.List; +import java.util.StringJoiner; +import java.util.stream.Collectors; +import racingcar.view.constants.ErrorMessage; + +public class FormatParser { + private FormatParser() { + } + + public static List split(String line, String delimiter) { + String[] values = line.split(delimiter); + List parsedValue = Arrays.stream(values) + .map(String::trim) + .collect(Collectors.toList()); + validateEmpty(parsedValue); + return null; + } + + public static String join(List values, String delimiter) { + StringJoiner joiner = new StringJoiner(delimiter); + values.forEach(joiner::add); + return joiner.toString(); + } + + private static void validateEmpty(List values) { + if (hasEmptyValue(values)) { + throw new IllegalArgumentException(ErrorMessage.FORMAT_EMPTY); + } + } + + private static boolean hasEmptyValue(List values) { + return values.stream() + .anyMatch(String::isEmpty); + } +} From 921f02ea245af829dddc9eb97d46db9317c47ed6 Mon Sep 17 00:00:00 2001 From: yoondgu Date: Wed, 14 Dec 2022 20:40:41 +0900 Subject: [PATCH 05/26] =?UTF-8?q?feat:=20=EA=B2=BD=EC=A3=BC=ED=95=A0=20?= =?UTF-8?q?=EB=AA=A8=EB=93=A0=20=EC=9E=90=EB=8F=99=EC=B0=A8=20=EC=9D=B4?= =?UTF-8?q?=EB=A6=84=EC=9D=84=20=ED=98=95=EC=8B=9D=EC=97=90=20=EB=A7=9E?= =?UTF-8?q?=EA=B2=8C=20=EC=9E=85=EB=A0=A5=EB=B0=9B=EB=8A=94=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/README.md | 2 +- src/main/java/racingcar/Application.java | 5 ++- .../controller/RacingController.java | 17 ++++++++++ src/main/java/racingcar/view/InputView.java | 31 +++++++++++++++++++ .../view/constants/ErrorMessage.java | 2 +- .../view/constants/InputMessage.java | 5 +++ .../racingcar/view/util/FormatParser.java | 2 +- 7 files changed, 60 insertions(+), 4 deletions(-) create mode 100644 src/main/java/racingcar/controller/RacingController.java create mode 100644 src/main/java/racingcar/view/InputView.java create mode 100644 src/main/java/racingcar/view/constants/InputMessage.java diff --git a/docs/README.md b/docs/README.md index ab46acd9e..4ab4fb993 100644 --- a/docs/README.md +++ b/docs/README.md @@ -45,7 +45,7 @@ ### UI 로직을 위한 기능 1. - - [ ] 경주할 모든 자동차 이름을 형식에 맞게 입력받는 기능 + - [x] 경주할 모든 자동차 이름을 형식에 맞게 입력받는 기능 - [x] `예외발생` : 빈 문자열 - [x] `예외발생` : 입력값 형식에 맞지 않음 2. diff --git a/src/main/java/racingcar/Application.java b/src/main/java/racingcar/Application.java index b9ed0456a..3db9dee21 100644 --- a/src/main/java/racingcar/Application.java +++ b/src/main/java/racingcar/Application.java @@ -1,7 +1,10 @@ package racingcar; +import racingcar.controller.RacingController; + public class Application { public static void main(String[] args) { - // TODO 구현 진행 + RacingController racingController = new RacingController(); + racingController.run(); } } diff --git a/src/main/java/racingcar/controller/RacingController.java b/src/main/java/racingcar/controller/RacingController.java new file mode 100644 index 000000000..ac3a4d3e0 --- /dev/null +++ b/src/main/java/racingcar/controller/RacingController.java @@ -0,0 +1,17 @@ +package racingcar.controller; + +import java.util.List; +import racingcar.view.InputView; + +public class RacingController { + private static final InputView inputView = new InputView(); + + public void run() { + enrollCarToRace(); + } + + public void enrollCarToRace() { + List carNames = inputView.inputCarNames(); + System.out.println(carNames); + } +} diff --git a/src/main/java/racingcar/view/InputView.java b/src/main/java/racingcar/view/InputView.java new file mode 100644 index 000000000..6cc38095d --- /dev/null +++ b/src/main/java/racingcar/view/InputView.java @@ -0,0 +1,31 @@ +package racingcar.view; + +import static racingcar.view.constants.Format.INPUT_NAMES_DELIMITER; + +import camp.nextstep.edu.missionutils.Console; +import java.util.List; +import racingcar.view.constants.ErrorMessage; +import racingcar.view.constants.InputMessage; +import racingcar.view.util.FormatParser; + +public class InputView { + public List inputCarNames() { + System.out.println(InputMessage.CAR_NAMES); + String line = Console.readLine(); + validateEmpty(line); + return FormatParser.split(line, INPUT_NAMES_DELIMITER); + } + + private void validateEmpty(String line) { + if (hasWrongFormat(line)) { + throw new IllegalArgumentException(ErrorMessage.FORMAT_INVALID); + } + } + + private boolean hasWrongFormat(String line) { + if (line.isEmpty()) { + return true; + } + return line.startsWith(INPUT_NAMES_DELIMITER) || line.endsWith(INPUT_NAMES_DELIMITER); + } +} diff --git a/src/main/java/racingcar/view/constants/ErrorMessage.java b/src/main/java/racingcar/view/constants/ErrorMessage.java index a820082c3..dac49e5d1 100644 --- a/src/main/java/racingcar/view/constants/ErrorMessage.java +++ b/src/main/java/racingcar/view/constants/ErrorMessage.java @@ -2,5 +2,5 @@ public class ErrorMessage { public static final String FORMAT_EMPTY = "형식 내의 값 중 빈 문자열이 있습니다."; - public static final String INPUT_EMPTY = "입력값이 빈 값입니다."; + public static final String FORMAT_INVALID = "입력값의 형식이 잘못되었습니다."; } diff --git a/src/main/java/racingcar/view/constants/InputMessage.java b/src/main/java/racingcar/view/constants/InputMessage.java new file mode 100644 index 000000000..cba0c5ab0 --- /dev/null +++ b/src/main/java/racingcar/view/constants/InputMessage.java @@ -0,0 +1,5 @@ +package racingcar.view.constants; + +public class InputMessage { + public static final String CAR_NAMES = "경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)"; +} diff --git a/src/main/java/racingcar/view/util/FormatParser.java b/src/main/java/racingcar/view/util/FormatParser.java index 4e8da4a7a..e2429d5c5 100644 --- a/src/main/java/racingcar/view/util/FormatParser.java +++ b/src/main/java/racingcar/view/util/FormatParser.java @@ -16,7 +16,7 @@ public static List split(String line, String delimiter) { .map(String::trim) .collect(Collectors.toList()); validateEmpty(parsedValue); - return null; + return parsedValue; } public static String join(List values, String delimiter) { From a805b40e863279557042e158073a739540549b94 Mon Sep 17 00:00:00 2001 From: yoondgu Date: Wed, 14 Dec 2022 20:54:49 +0900 Subject: [PATCH 06/26] =?UTF-8?q?feat:=20=EA=B2=BD=EC=A3=BC=ED=95=A0=20?= =?UTF-8?q?=EB=AA=A8=EB=93=A0=20=EC=9E=90=EB=8F=99=EC=B0=A8=EC=9D=98=20?= =?UTF-8?q?=EC=9D=B4=EB=A6=84=EC=9D=84=20=EC=A0=80=EC=9E=A5=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/README.md | 13 +++++---- src/main/java/racingcar/Car.java | 12 -------- .../controller/RacingController.java | 4 ++- src/main/java/racingcar/model/RacingGame.java | 22 +++++++++++++++ .../model/constants/ErrorMessage.java | 6 ++++ .../racingcar/model/constants/GameRule.java | 6 ++++ src/main/java/racingcar/model/domain/Car.java | 28 +++++++++++++++++++ src/main/java/racingcar/view/OutputView.java | 4 +++ 8 files changed, 77 insertions(+), 18 deletions(-) delete mode 100644 src/main/java/racingcar/Car.java create mode 100644 src/main/java/racingcar/model/RacingGame.java create mode 100644 src/main/java/racingcar/model/constants/ErrorMessage.java create mode 100644 src/main/java/racingcar/model/constants/GameRule.java create mode 100644 src/main/java/racingcar/model/domain/Car.java create mode 100644 src/main/java/racingcar/view/OutputView.java diff --git a/docs/README.md b/docs/README.md index 4ab4fb993..979831e10 100644 --- a/docs/README.md +++ b/docs/README.md @@ -29,9 +29,9 @@ ### 도메인 로직을 위한 기능 1. - - [ ] 경주할 모든 자동차의 이름을 저장하는 기능 - - [ ] `예외발생` : 이름의 길이가 5자 초과 - - [ ] `예외발생` : 경주할 자동차가 1대 + - [x] 경주할 모든 자동차의 이름을 저장하는 기능 + - [x] `예외발생` : 경주할 자동차가 1대 + - [x] `예외발생` : 이름의 길이가 5자 초과 2. - [ ] 랜덤 숫자를 생성하는 기능 3. @@ -67,12 +67,15 @@ ### 도메인 로직 정보 - [ ] 이동 숫자 0 ~ 9, 전진 조건 4 -- [ ] 이름 길이 제한 5 +- [x] 이름 길이 상한 제한 5 +- [x] 자동차 개수 하한 제한 2 +- [ ] 에러 메시지 ### UI 로직 정보 -- [ ] 구분자 쉼표 +- [x] 구분자 쉼표 - [ ] 입력 안내 메시지 +- [ ] 에러 메시지 - [ ] 실행 결과 출력 형식 - [ ] 우승자 안내 문구 출력 형식 - [ ] 에러메시지 출력 형식 diff --git a/src/main/java/racingcar/Car.java b/src/main/java/racingcar/Car.java deleted file mode 100644 index ab3df9492..000000000 --- a/src/main/java/racingcar/Car.java +++ /dev/null @@ -1,12 +0,0 @@ -package racingcar; - -public class Car { - private final String name; - private int position = 0; - - public Car(String name) { - this.name = name; - } - - // 추가 기능 구현 -} diff --git a/src/main/java/racingcar/controller/RacingController.java b/src/main/java/racingcar/controller/RacingController.java index ac3a4d3e0..21175eb30 100644 --- a/src/main/java/racingcar/controller/RacingController.java +++ b/src/main/java/racingcar/controller/RacingController.java @@ -1,10 +1,12 @@ package racingcar.controller; import java.util.List; +import racingcar.model.RacingGame; import racingcar.view.InputView; public class RacingController { private static final InputView inputView = new InputView(); + private static final RacingGame racingGame = new RacingGame(); public void run() { enrollCarToRace(); @@ -12,6 +14,6 @@ public void run() { public void enrollCarToRace() { List carNames = inputView.inputCarNames(); - System.out.println(carNames); + racingGame.enrollCars(carNames); } } diff --git a/src/main/java/racingcar/model/RacingGame.java b/src/main/java/racingcar/model/RacingGame.java new file mode 100644 index 000000000..3a8431d90 --- /dev/null +++ b/src/main/java/racingcar/model/RacingGame.java @@ -0,0 +1,22 @@ +package racingcar.model; + +import java.util.ArrayList; +import java.util.List; +import racingcar.model.constants.ErrorMessage; +import racingcar.model.constants.GameRule; +import racingcar.model.domain.Car; + +public class RacingGame { + private static final List racingCars = new ArrayList<>(); + + public void enrollCars(List carNames) { + validateCarCount(carNames.size()); + carNames.forEach(name -> racingCars.add(new Car(name))); + } + + private void validateCarCount(int count) { + if (count < GameRule.CARS_COUNT_LOWER_LIMIT) { + throw new IllegalArgumentException(ErrorMessage.CARS_LACK_OF_COUNT); + } + } +} diff --git a/src/main/java/racingcar/model/constants/ErrorMessage.java b/src/main/java/racingcar/model/constants/ErrorMessage.java new file mode 100644 index 000000000..52ffec221 --- /dev/null +++ b/src/main/java/racingcar/model/constants/ErrorMessage.java @@ -0,0 +1,6 @@ +package racingcar.model.constants; + +public class ErrorMessage { + public static final String CARS_LACK_OF_COUNT = "경주할 자동차는 최소 2대 필요합니다."; + public static final String CAR_NAME_LENGTH_OVER = "자동차 이름은 최대 5글자까지만 가능합니다."; +} diff --git a/src/main/java/racingcar/model/constants/GameRule.java b/src/main/java/racingcar/model/constants/GameRule.java new file mode 100644 index 000000000..74ace0793 --- /dev/null +++ b/src/main/java/racingcar/model/constants/GameRule.java @@ -0,0 +1,6 @@ +package racingcar.model.constants; + +public class GameRule { + public static final int NAME_LENGTH_UPPER_LIMIT = 5; + public static final int CARS_COUNT_LOWER_LIMIT = 2; +} diff --git a/src/main/java/racingcar/model/domain/Car.java b/src/main/java/racingcar/model/domain/Car.java new file mode 100644 index 000000000..8e308a29a --- /dev/null +++ b/src/main/java/racingcar/model/domain/Car.java @@ -0,0 +1,28 @@ +package racingcar.model.domain; + +import racingcar.model.constants.ErrorMessage; +import racingcar.model.constants.GameRule; + +public class Car { + private final String name; + private final int position = 0; + + public Car(String name) { + validateName(name); + this.name = name; + } + + private void validateName(String name) { + if (name.length() > GameRule.NAME_LENGTH_UPPER_LIMIT) { + throw new IllegalArgumentException(ErrorMessage.CAR_NAME_LENGTH_OVER); + } + } + + @Override + public String toString() { + return "Car{" + + "name='" + name + '\'' + + ", position=" + position + + '}'; + } +} diff --git a/src/main/java/racingcar/view/OutputView.java b/src/main/java/racingcar/view/OutputView.java new file mode 100644 index 000000000..abf938a66 --- /dev/null +++ b/src/main/java/racingcar/view/OutputView.java @@ -0,0 +1,4 @@ +package racingcar.view; + +public class OutputView { +} From 56ec0a5f94c2632a36c0a7ba46ca3837b4046bba Mon Sep 17 00:00:00 2001 From: yoondgu Date: Wed, 14 Dec 2022 21:03:04 +0900 Subject: [PATCH 07/26] =?UTF-8?q?feat:=20=EB=9E=9C=EB=8D=A4=20=EC=88=AB?= =?UTF-8?q?=EC=9E=90=EB=A5=BC=20=EC=83=9D=EC=84=B1=ED=95=98=EB=8A=94=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/README.md | 2 +- .../java/racingcar/controller/RacingController.java | 3 ++- src/main/java/racingcar/model/NumberGenerator.java | 6 ++++++ src/main/java/racingcar/model/RacingGame.java | 7 ++++++- .../java/racingcar/model/RandomNumberGenerator.java | 13 +++++++++++++ 5 files changed, 28 insertions(+), 3 deletions(-) create mode 100644 src/main/java/racingcar/model/NumberGenerator.java create mode 100644 src/main/java/racingcar/model/RandomNumberGenerator.java diff --git a/docs/README.md b/docs/README.md index 979831e10..7e04b9f4a 100644 --- a/docs/README.md +++ b/docs/README.md @@ -33,7 +33,7 @@ - [x] `예외발생` : 경주할 자동차가 1대 - [x] `예외발생` : 이름의 길이가 5자 초과 2. - - [ ] 랜덤 숫자를 생성하는 기능 + - [x] 랜덤 숫자를 생성하는 기능 3. - [ ] 자동차가 전달받은 숫자에 따라 이동(전진하거나 전진하지않음)하는 기능 - [ ] `예외발생` : 전달받은 숫자가 범위 밖 diff --git a/src/main/java/racingcar/controller/RacingController.java b/src/main/java/racingcar/controller/RacingController.java index 21175eb30..48a968697 100644 --- a/src/main/java/racingcar/controller/RacingController.java +++ b/src/main/java/racingcar/controller/RacingController.java @@ -2,11 +2,12 @@ import java.util.List; import racingcar.model.RacingGame; +import racingcar.model.RandomNumberGenerator; import racingcar.view.InputView; public class RacingController { private static final InputView inputView = new InputView(); - private static final RacingGame racingGame = new RacingGame(); + private static final RacingGame racingGame = new RacingGame(new RandomNumberGenerator()); public void run() { enrollCarToRace(); diff --git a/src/main/java/racingcar/model/NumberGenerator.java b/src/main/java/racingcar/model/NumberGenerator.java new file mode 100644 index 000000000..817bb2485 --- /dev/null +++ b/src/main/java/racingcar/model/NumberGenerator.java @@ -0,0 +1,6 @@ +package racingcar.model; + +@FunctionalInterface +public interface NumberGenerator { + int make(); +} diff --git a/src/main/java/racingcar/model/RacingGame.java b/src/main/java/racingcar/model/RacingGame.java index 3a8431d90..c1011de96 100644 --- a/src/main/java/racingcar/model/RacingGame.java +++ b/src/main/java/racingcar/model/RacingGame.java @@ -7,7 +7,12 @@ import racingcar.model.domain.Car; public class RacingGame { - private static final List racingCars = new ArrayList<>(); + private final List racingCars = new ArrayList<>(); + private final NumberGenerator numberGenerator; + + public RacingGame(NumberGenerator numberGenerator) { + this.numberGenerator = numberGenerator; + } public void enrollCars(List carNames) { validateCarCount(carNames.size()); diff --git a/src/main/java/racingcar/model/RandomNumberGenerator.java b/src/main/java/racingcar/model/RandomNumberGenerator.java new file mode 100644 index 000000000..96c280c92 --- /dev/null +++ b/src/main/java/racingcar/model/RandomNumberGenerator.java @@ -0,0 +1,13 @@ +package racingcar.model; + +import camp.nextstep.edu.missionutils.Randoms; + +public class RandomNumberGenerator implements NumberGenerator { + private static final int RANGE_START = 0; + private static final int RANGE_END = 9; + + @Override + public int make() { + return Randoms.pickNumberInRange(RANGE_START, RANGE_END); + } +} From f2d58e927e727349025cf59cd298a4caf5a7ff5d Mon Sep 17 00:00:00 2001 From: yoondgu Date: Wed, 14 Dec 2022 21:11:25 +0900 Subject: [PATCH 08/26] =?UTF-8?q?feat:=20=EC=9E=90=EB=8F=99=EC=B0=A8?= =?UTF-8?q?=EA=B0=80=20=EC=A0=84=EB=8B=AC=EB=B0=9B=EC=9D=80=20=EC=88=AB?= =?UTF-8?q?=EC=9E=90=EC=97=90=20=EB=94=B0=EB=9D=BC=20=EC=9D=B4=EB=8F=99?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/README.md | 3 +-- .../controller/RacingController.java | 1 + src/main/java/racingcar/dto/CarDTO.java | 27 +++++++++++++++++++ src/main/java/racingcar/model/RacingGame.java | 9 +++++++ .../racingcar/model/constants/GameRule.java | 1 + src/main/java/racingcar/model/domain/Car.java | 17 +++++++----- 6 files changed, 49 insertions(+), 9 deletions(-) create mode 100644 src/main/java/racingcar/dto/CarDTO.java diff --git a/docs/README.md b/docs/README.md index 7e04b9f4a..18f9653fc 100644 --- a/docs/README.md +++ b/docs/README.md @@ -35,8 +35,7 @@ 2. - [x] 랜덤 숫자를 생성하는 기능 3. - - [ ] 자동차가 전달받은 숫자에 따라 이동(전진하거나 전진하지않음)하는 기능 - - [ ] `예외발생` : 전달받은 숫자가 범위 밖 + - [x] 자동차가 전달받은 숫자에 따라 이동(전진하거나 전진하지않음)하는 기능 4. - [ ] 자동차 간 위치를 비교해 가장 값이 큰, 최소 1명 최대 n명의 우승자를 구하는 기능 5. diff --git a/src/main/java/racingcar/controller/RacingController.java b/src/main/java/racingcar/controller/RacingController.java index 48a968697..82d91b5b3 100644 --- a/src/main/java/racingcar/controller/RacingController.java +++ b/src/main/java/racingcar/controller/RacingController.java @@ -11,6 +11,7 @@ public class RacingController { public void run() { enrollCarToRace(); + System.out.println(racingGame.moveCars()); } public void enrollCarToRace() { diff --git a/src/main/java/racingcar/dto/CarDTO.java b/src/main/java/racingcar/dto/CarDTO.java new file mode 100644 index 000000000..02ededbbf --- /dev/null +++ b/src/main/java/racingcar/dto/CarDTO.java @@ -0,0 +1,27 @@ +package racingcar.dto; + +public class CarDTO { + private final String name; + private final int position; + + public CarDTO(String name, int position) { + this.name = name; + this.position = position; + } + + public String getName() { + return name; + } + + public int getPosition() { + return position; + } + + @Override + public String toString() { + return "CarDTO{" + + "name='" + name + '\'' + + ", position=" + position + + '}'; + } +} diff --git a/src/main/java/racingcar/model/RacingGame.java b/src/main/java/racingcar/model/RacingGame.java index c1011de96..697c42952 100644 --- a/src/main/java/racingcar/model/RacingGame.java +++ b/src/main/java/racingcar/model/RacingGame.java @@ -2,6 +2,8 @@ import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; +import racingcar.dto.CarDTO; import racingcar.model.constants.ErrorMessage; import racingcar.model.constants.GameRule; import racingcar.model.domain.Car; @@ -24,4 +26,11 @@ private void validateCarCount(int count) { throw new IllegalArgumentException(ErrorMessage.CARS_LACK_OF_COUNT); } } + + public List moveCars() { + racingCars.forEach(car -> car.move(numberGenerator.make())); + return racingCars.stream() + .map(Car::to) + .collect(Collectors.toList()); + } } diff --git a/src/main/java/racingcar/model/constants/GameRule.java b/src/main/java/racingcar/model/constants/GameRule.java index 74ace0793..a5dfe5c3b 100644 --- a/src/main/java/racingcar/model/constants/GameRule.java +++ b/src/main/java/racingcar/model/constants/GameRule.java @@ -3,4 +3,5 @@ public class GameRule { public static final int NAME_LENGTH_UPPER_LIMIT = 5; public static final int CARS_COUNT_LOWER_LIMIT = 2; + public static final int CAR_FORWARD_LOWER_LIMIT = 4; } diff --git a/src/main/java/racingcar/model/domain/Car.java b/src/main/java/racingcar/model/domain/Car.java index 8e308a29a..5661c66e0 100644 --- a/src/main/java/racingcar/model/domain/Car.java +++ b/src/main/java/racingcar/model/domain/Car.java @@ -1,11 +1,12 @@ package racingcar.model.domain; +import racingcar.dto.CarDTO; import racingcar.model.constants.ErrorMessage; import racingcar.model.constants.GameRule; public class Car { private final String name; - private final int position = 0; + private int position = 0; public Car(String name) { validateName(name); @@ -18,11 +19,13 @@ private void validateName(String name) { } } - @Override - public String toString() { - return "Car{" + - "name='" + name + '\'' + - ", position=" + position + - '}'; + public void move(int number) { + if (number > GameRule.CAR_FORWARD_LOWER_LIMIT) { + position++; + } + } + + public CarDTO to() { + return new CarDTO(name, position); } } From 24e66d7ea1cdc4d7d28a2ef447a69d9e4e18570f Mon Sep 17 00:00:00 2001 From: yoondgu Date: Wed, 14 Dec 2022 21:20:10 +0900 Subject: [PATCH 09/26] =?UTF-8?q?feat:=20=EC=8B=9C=EB=8F=84=ED=95=A0=20?= =?UTF-8?q?=ED=9A=8C=EC=88=98=EB=A5=BC=20=EC=9E=85=EB=A0=A5=EB=B0=9B?= =?UTF-8?q?=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/README.md | 4 ++-- .../controller/RacingController.java | 7 ++++++- src/main/java/racingcar/view/InputView.java | 8 +++++++ .../view/constants/ErrorMessage.java | 2 ++ .../view/constants/InputMessage.java | 1 + .../racingcar/view/util/NumberParser.java | 21 +++++++++++++++++++ 6 files changed, 40 insertions(+), 3 deletions(-) create mode 100644 src/main/java/racingcar/view/util/NumberParser.java diff --git a/docs/README.md b/docs/README.md index 18f9653fc..7eed3eb75 100644 --- a/docs/README.md +++ b/docs/README.md @@ -48,8 +48,8 @@ - [x] `예외발생` : 빈 문자열 - [x] `예외발생` : 입력값 형식에 맞지 않음 2. - - [ ] 시도할 회수를 입력받는 기능 - - [ ] `예외발생` : 자연수가 아닌 값 + - [x] 시도할 회수를 입력받는 기능 + - [x] `예외발생` : 자연수가 아닌 값 3. - [ ] 차수 별 실행 결과 출력하는 기능 4. diff --git a/src/main/java/racingcar/controller/RacingController.java b/src/main/java/racingcar/controller/RacingController.java index 82d91b5b3..b45309b3b 100644 --- a/src/main/java/racingcar/controller/RacingController.java +++ b/src/main/java/racingcar/controller/RacingController.java @@ -11,11 +11,16 @@ public class RacingController { public void run() { enrollCarToRace(); - System.out.println(racingGame.moveCars()); + moveCarsByCount(); } public void enrollCarToRace() { List carNames = inputView.inputCarNames(); racingGame.enrollCars(carNames); } + + public void moveCarsByCount() { + int moveCount = inputView.inputMoveCount(); + // TODO moveCount 만큼 반복 이동 + } } diff --git a/src/main/java/racingcar/view/InputView.java b/src/main/java/racingcar/view/InputView.java index 6cc38095d..16d060957 100644 --- a/src/main/java/racingcar/view/InputView.java +++ b/src/main/java/racingcar/view/InputView.java @@ -7,6 +7,7 @@ import racingcar.view.constants.ErrorMessage; import racingcar.view.constants.InputMessage; import racingcar.view.util.FormatParser; +import racingcar.view.util.NumberParser; public class InputView { public List inputCarNames() { @@ -28,4 +29,11 @@ private boolean hasWrongFormat(String line) { } return line.startsWith(INPUT_NAMES_DELIMITER) || line.endsWith(INPUT_NAMES_DELIMITER); } + + public int inputMoveCount() { + System.out.println(InputMessage.MOVE_COUNT); + String line = Console.readLine(); + System.out.println(); + return NumberParser.parseDigit(line); + } } diff --git a/src/main/java/racingcar/view/constants/ErrorMessage.java b/src/main/java/racingcar/view/constants/ErrorMessage.java index dac49e5d1..e77f4f23b 100644 --- a/src/main/java/racingcar/view/constants/ErrorMessage.java +++ b/src/main/java/racingcar/view/constants/ErrorMessage.java @@ -3,4 +3,6 @@ public class ErrorMessage { public static final String FORMAT_EMPTY = "형식 내의 값 중 빈 문자열이 있습니다."; public static final String FORMAT_INVALID = "입력값의 형식이 잘못되었습니다."; + public static final String NOT_A_DIGIT = "입력값이 자연수가 아닙니다."; + public static final String NOT_A_NUMBER = "입력값이 숫자가 아닙니다."; } diff --git a/src/main/java/racingcar/view/constants/InputMessage.java b/src/main/java/racingcar/view/constants/InputMessage.java index cba0c5ab0..ce09f2526 100644 --- a/src/main/java/racingcar/view/constants/InputMessage.java +++ b/src/main/java/racingcar/view/constants/InputMessage.java @@ -2,4 +2,5 @@ public class InputMessage { public static final String CAR_NAMES = "경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)"; + public static final String MOVE_COUNT = "시도할 회수는 몇회인가요?"; } diff --git a/src/main/java/racingcar/view/util/NumberParser.java b/src/main/java/racingcar/view/util/NumberParser.java new file mode 100644 index 000000000..783dcf545 --- /dev/null +++ b/src/main/java/racingcar/view/util/NumberParser.java @@ -0,0 +1,21 @@ +package racingcar.view.util; + +import racingcar.view.constants.ErrorMessage; + +public class NumberParser { + public static int parseDigit(String value) { + int number = parseInteger(value); + if (number <= 0) { + throw new IllegalArgumentException(ErrorMessage.NOT_A_DIGIT); + } + return number; + } + + public static int parseInteger(String value) { + try { + return Integer.parseInt(value); + } catch (NumberFormatException e) { + throw new IllegalArgumentException(ErrorMessage.NOT_A_NUMBER); + } + } +} From 7c43478566e048bc76acdb17753705b94bd375fa Mon Sep 17 00:00:00 2001 From: yoondgu Date: Wed, 14 Dec 2022 21:22:55 +0900 Subject: [PATCH 10/26] =?UTF-8?q?fix:=20=EC=9E=90=EB=8F=99=EC=B0=A8=20?= =?UTF-8?q?=EC=9D=B4=EB=A6=84=20=EC=A0=80=EC=9E=A5=20=EA=B8=B0=EB=8A=A5?= =?UTF-8?q?=EC=97=90=20=EC=A4=91=EB=B3=B5=20=EA=B2=80=EC=A6=9D=20=EB=A1=9C?= =?UTF-8?q?=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 --- docs/README.md | 1 + src/main/java/racingcar/model/RacingGame.java | 10 +++++++--- .../java/racingcar/model/constants/ErrorMessage.java | 1 + 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/docs/README.md b/docs/README.md index 7eed3eb75..6face6b91 100644 --- a/docs/README.md +++ b/docs/README.md @@ -32,6 +32,7 @@ - [x] 경주할 모든 자동차의 이름을 저장하는 기능 - [x] `예외발생` : 경주할 자동차가 1대 - [x] `예외발생` : 이름의 길이가 5자 초과 + - [x] `예외발생` : 중복 이름이 존재하는 경우 2. - [x] 랜덤 숫자를 생성하는 기능 3. diff --git a/src/main/java/racingcar/model/RacingGame.java b/src/main/java/racingcar/model/RacingGame.java index 697c42952..ef1dd7eaf 100644 --- a/src/main/java/racingcar/model/RacingGame.java +++ b/src/main/java/racingcar/model/RacingGame.java @@ -1,6 +1,7 @@ package racingcar.model; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.stream.Collectors; import racingcar.dto.CarDTO; @@ -17,14 +18,17 @@ public RacingGame(NumberGenerator numberGenerator) { } public void enrollCars(List carNames) { - validateCarCount(carNames.size()); + validateCarNames(carNames); carNames.forEach(name -> racingCars.add(new Car(name))); } - private void validateCarCount(int count) { - if (count < GameRule.CARS_COUNT_LOWER_LIMIT) { + private void validateCarNames(List carNames) { + if (carNames.size() < GameRule.CARS_COUNT_LOWER_LIMIT) { throw new IllegalArgumentException(ErrorMessage.CARS_LACK_OF_COUNT); } + if (carNames.size() != new HashSet<>(carNames).size()) { + throw new IllegalArgumentException(ErrorMessage.CARS_DUPLICATED_NAME); + } } public List moveCars() { diff --git a/src/main/java/racingcar/model/constants/ErrorMessage.java b/src/main/java/racingcar/model/constants/ErrorMessage.java index 52ffec221..4b10f4c8d 100644 --- a/src/main/java/racingcar/model/constants/ErrorMessage.java +++ b/src/main/java/racingcar/model/constants/ErrorMessage.java @@ -3,4 +3,5 @@ public class ErrorMessage { public static final String CARS_LACK_OF_COUNT = "경주할 자동차는 최소 2대 필요합니다."; public static final String CAR_NAME_LENGTH_OVER = "자동차 이름은 최대 5글자까지만 가능합니다."; + public static final String CARS_DUPLICATED_NAME = "중복된 자동차 이름을 등록할 수 없습니다."; } From d081b01312818c09b770b0265c50f060935fde38 Mon Sep 17 00:00:00 2001 From: yoondgu Date: Wed, 14 Dec 2022 21:27:27 +0900 Subject: [PATCH 11/26] =?UTF-8?q?test:=20=EC=9E=90=EB=8F=99=EC=B0=A8=20?= =?UTF-8?q?=EC=9D=B4=EB=A6=84=20=EC=A0=80=EC=9E=A5=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=EC=BD=94=EB=93=9C=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/racingcar/model/RacingGameTest.java | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 src/test/java/racingcar/model/RacingGameTest.java diff --git a/src/test/java/racingcar/model/RacingGameTest.java b/src/test/java/racingcar/model/RacingGameTest.java new file mode 100644 index 000000000..fa273f83a --- /dev/null +++ b/src/test/java/racingcar/model/RacingGameTest.java @@ -0,0 +1,33 @@ +package racingcar.model; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.util.List; +import org.junit.jupiter.api.Test; + +class RacingGameTest { + @Test + void 자동차_등록_개수_예외발생() { + RacingGame racingGame = new RacingGame(new RandomNumberGenerator()); + + assertThatThrownBy(() -> racingGame.enrollCars(List.of("자동차명"))) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + void 자동차_등록_이름길이_예외발생() { + RacingGame racingGame = new RacingGame(new RandomNumberGenerator()); + + assertThatThrownBy(() -> racingGame.enrollCars(List.of("자동차명여섯"))) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + void 자동차_등록_중복이름_예외발생() { + RacingGame racingGame = new RacingGame(new RandomNumberGenerator()); + + assertThatThrownBy(() -> racingGame.enrollCars(List.of("자동차명", "자동차명"))) + .isInstanceOf(IllegalArgumentException.class); + + } +} \ No newline at end of file From a94e4268c2e23d12114755d9c930af986b1da5e5 Mon Sep 17 00:00:00 2001 From: yoondgu Date: Wed, 14 Dec 2022 21:31:35 +0900 Subject: [PATCH 12/26] =?UTF-8?q?fix:=20=EC=9E=90=EB=8F=99=EC=B0=A8=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99=20=EC=98=A4=EA=B8=B0=EB=8A=A5=20=EC=88=98?= =?UTF-8?q?=EC=A0=95,=20=ED=85=8C=EC=8A=A4=ED=8A=B8=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/racingcar/model/domain/Car.java | 2 +- .../java/racingcar/model/domain/CarTest.java | 23 +++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 src/test/java/racingcar/model/domain/CarTest.java diff --git a/src/main/java/racingcar/model/domain/Car.java b/src/main/java/racingcar/model/domain/Car.java index 5661c66e0..3eea37c29 100644 --- a/src/main/java/racingcar/model/domain/Car.java +++ b/src/main/java/racingcar/model/domain/Car.java @@ -20,7 +20,7 @@ private void validateName(String name) { } public void move(int number) { - if (number > GameRule.CAR_FORWARD_LOWER_LIMIT) { + if (number >= GameRule.CAR_FORWARD_LOWER_LIMIT) { position++; } } diff --git a/src/test/java/racingcar/model/domain/CarTest.java b/src/test/java/racingcar/model/domain/CarTest.java new file mode 100644 index 000000000..bf8f47960 --- /dev/null +++ b/src/test/java/racingcar/model/domain/CarTest.java @@ -0,0 +1,23 @@ +package racingcar.model.domain; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +class CarTest { + @Test + void 이동_전진() { + Car car = new Car("테스트"); + car.move(4); + + assertThat(car.to().getPosition()).isEqualTo(1); + } + + @Test + void 이동_정지() { + Car car = new Car("테스트"); + car.move(3); + + assertThat(car.to().getPosition()).isEqualTo(0); + } +} \ No newline at end of file From d6604d6112f24e4d5fbd20f7273fa4f2be4fd0d2 Mon Sep 17 00:00:00 2001 From: yoondgu Date: Wed, 14 Dec 2022 21:36:28 +0900 Subject: [PATCH 13/26] =?UTF-8?q?feat:=20=EC=A0=84=EB=8B=AC=EB=B0=9B?= =?UTF-8?q?=EC=9D=80=20=ED=9A=8C=EC=88=98=EB=A7=8C=ED=81=BC=20=EC=9E=90?= =?UTF-8?q?=EB=8F=99=EC=B0=A8=20=EC=9D=B4=EB=8F=99=EC=9D=84=20=EC=8B=A4?= =?UTF-8?q?=ED=96=89=EC=8B=9C=ED=82=A4=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/README.md | 4 ++-- .../racingcar/controller/RacingController.java | 7 ++++--- src/main/java/racingcar/dto/CarStatusDTO.java | 15 +++++++++++++++ src/main/java/racingcar/model/RacingGame.java | 11 ++++++++++- 4 files changed, 31 insertions(+), 6 deletions(-) create mode 100644 src/main/java/racingcar/dto/CarStatusDTO.java diff --git a/docs/README.md b/docs/README.md index 6face6b91..6b1b816c1 100644 --- a/docs/README.md +++ b/docs/README.md @@ -40,7 +40,7 @@ 4. - [ ] 자동차 간 위치를 비교해 가장 값이 큰, 최소 1명 최대 n명의 우승자를 구하는 기능 5. - - [ ] 전달받은 회수 만큼 자동차 이동을 실행시키는 기능 + - [x] 전달받은 회수 만큼 자동차 이동을 실행시키는 기능 ### UI 로직을 위한 기능 @@ -66,7 +66,7 @@ ### 도메인 로직 정보 -- [ ] 이동 숫자 0 ~ 9, 전진 조건 4 +- [x] 이동 숫자 0 ~ 9, 전진 조건 4 - [x] 이름 길이 상한 제한 5 - [x] 자동차 개수 하한 제한 2 - [ ] 에러 메시지 diff --git a/src/main/java/racingcar/controller/RacingController.java b/src/main/java/racingcar/controller/RacingController.java index b45309b3b..8350889b0 100644 --- a/src/main/java/racingcar/controller/RacingController.java +++ b/src/main/java/racingcar/controller/RacingController.java @@ -1,13 +1,14 @@ package racingcar.controller; import java.util.List; +import racingcar.dto.CarStatusDTO; import racingcar.model.RacingGame; import racingcar.model.RandomNumberGenerator; import racingcar.view.InputView; public class RacingController { - private static final InputView inputView = new InputView(); - private static final RacingGame racingGame = new RacingGame(new RandomNumberGenerator()); + private final InputView inputView = new InputView(); + private final RacingGame racingGame = new RacingGame(new RandomNumberGenerator()); public void run() { enrollCarToRace(); @@ -21,6 +22,6 @@ public void enrollCarToRace() { public void moveCarsByCount() { int moveCount = inputView.inputMoveCount(); - // TODO moveCount 만큼 반복 이동 + List carStatuses = racingGame.repeatMovingCars(moveCount); } } diff --git a/src/main/java/racingcar/dto/CarStatusDTO.java b/src/main/java/racingcar/dto/CarStatusDTO.java new file mode 100644 index 000000000..2dbc7720b --- /dev/null +++ b/src/main/java/racingcar/dto/CarStatusDTO.java @@ -0,0 +1,15 @@ +package racingcar.dto; + +import java.util.List; + +public class CarStatusDTO { + private final List cars; + + public CarStatusDTO(List cars) { + this.cars = cars; + } + + public List getCars() { + return cars; + } +} diff --git a/src/main/java/racingcar/model/RacingGame.java b/src/main/java/racingcar/model/RacingGame.java index ef1dd7eaf..05869c861 100644 --- a/src/main/java/racingcar/model/RacingGame.java +++ b/src/main/java/racingcar/model/RacingGame.java @@ -5,6 +5,7 @@ import java.util.List; import java.util.stream.Collectors; import racingcar.dto.CarDTO; +import racingcar.dto.CarStatusDTO; import racingcar.model.constants.ErrorMessage; import racingcar.model.constants.GameRule; import racingcar.model.domain.Car; @@ -31,7 +32,15 @@ private void validateCarNames(List carNames) { } } - public List moveCars() { + public List repeatMovingCars(int moveCount) { + List carStatuses = new ArrayList<>(); + for (int count = 0; count < moveCount; count++) { + carStatuses.add(new CarStatusDTO(moveCars())); + } + return carStatuses; + } + + private List moveCars() { racingCars.forEach(car -> car.move(numberGenerator.make())); return racingCars.stream() .map(Car::to) From bc100148b293dd536310fa21e2ff5bf026153043 Mon Sep 17 00:00:00 2001 From: yoondgu Date: Wed, 14 Dec 2022 21:58:14 +0900 Subject: [PATCH 14/26] =?UTF-8?q?feat:=20=EC=B0=A8=EC=88=98=20=EB=B3=84=20?= =?UTF-8?q?=EC=8B=A4=ED=96=89=20=EA=B2=B0=EA=B3=BC=20=EC=B6=9C=EB=A0=A5?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/README.md | 4 ++-- .../controller/RacingController.java | 3 +++ src/main/java/racingcar/view/OutputView.java | 22 +++++++++++++++++++ .../java/racingcar/view/constants/Format.java | 2 ++ .../view/constants/OutputMessage.java | 5 +++++ .../racingcar/view/util/FormatParser.java | 4 ++++ 6 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 src/main/java/racingcar/view/constants/OutputMessage.java diff --git a/docs/README.md b/docs/README.md index 6b1b816c1..85901355e 100644 --- a/docs/README.md +++ b/docs/README.md @@ -52,7 +52,7 @@ - [x] 시도할 회수를 입력받는 기능 - [x] `예외발생` : 자연수가 아닌 값 3. - - [ ] 차수 별 실행 결과 출력하는 기능 + - [x] 차수 별 실행 결과 출력하는 기능 4. - [ ] 우승자 안내 문구 출력하는 기능 5. @@ -76,7 +76,7 @@ - [x] 구분자 쉼표 - [ ] 입력 안내 메시지 - [ ] 에러 메시지 -- [ ] 실행 결과 출력 형식 +- [x] 실행 결과 출력 형식 - [ ] 우승자 안내 문구 출력 형식 - [ ] 에러메시지 출력 형식 diff --git a/src/main/java/racingcar/controller/RacingController.java b/src/main/java/racingcar/controller/RacingController.java index 8350889b0..6e05c6924 100644 --- a/src/main/java/racingcar/controller/RacingController.java +++ b/src/main/java/racingcar/controller/RacingController.java @@ -5,9 +5,11 @@ import racingcar.model.RacingGame; import racingcar.model.RandomNumberGenerator; import racingcar.view.InputView; +import racingcar.view.OutputView; public class RacingController { private final InputView inputView = new InputView(); + private final OutputView outputView = new OutputView(); private final RacingGame racingGame = new RacingGame(new RandomNumberGenerator()); public void run() { @@ -23,5 +25,6 @@ public void enrollCarToRace() { public void moveCarsByCount() { int moveCount = inputView.inputMoveCount(); List carStatuses = racingGame.repeatMovingCars(moveCount); + outputView.printGameResult(carStatuses); } } diff --git a/src/main/java/racingcar/view/OutputView.java b/src/main/java/racingcar/view/OutputView.java index abf938a66..99a49f279 100644 --- a/src/main/java/racingcar/view/OutputView.java +++ b/src/main/java/racingcar/view/OutputView.java @@ -1,4 +1,26 @@ package racingcar.view; +import java.util.List; +import racingcar.dto.CarDTO; +import racingcar.dto.CarStatusDTO; +import racingcar.view.constants.Format; +import racingcar.view.constants.OutputMessage; +import racingcar.view.util.FormatParser; + public class OutputView { + public void printGameResult(List carStatuses) { + System.out.println(OutputMessage.GAME_RESULT); + carStatuses.forEach(this::printCarStatuses); + } + + private void printCarStatuses(CarStatusDTO carStatusDTO) { + List cars = carStatusDTO.getCars(); + cars.forEach(this::printCar); + System.out.println(); + } + + private void printCar(CarDTO carDTO) { + System.out.printf(Format.OUTPUT_CAR_STATUS_FORMAT, carDTO.getName(), + FormatParser.make(carDTO.getPosition(), Format.OUTPUT_CAR_POSITION_UNIT)); + } } diff --git a/src/main/java/racingcar/view/constants/Format.java b/src/main/java/racingcar/view/constants/Format.java index d592389e8..d429e6a3a 100644 --- a/src/main/java/racingcar/view/constants/Format.java +++ b/src/main/java/racingcar/view/constants/Format.java @@ -3,4 +3,6 @@ public class Format { public static final String INPUT_NAMES_DELIMITER = ","; public static final String OUTPUT_NAMES_DELIMITER = ", "; + public static final String OUTPUT_CAR_STATUS_FORMAT = "%s : %s" + System.lineSeparator(); + public static final String OUTPUT_CAR_POSITION_UNIT = "-"; } diff --git a/src/main/java/racingcar/view/constants/OutputMessage.java b/src/main/java/racingcar/view/constants/OutputMessage.java new file mode 100644 index 000000000..6ec77ea7d --- /dev/null +++ b/src/main/java/racingcar/view/constants/OutputMessage.java @@ -0,0 +1,5 @@ +package racingcar.view.constants; + +public class OutputMessage { + public static final String GAME_RESULT = "실행 결과"; +} diff --git a/src/main/java/racingcar/view/util/FormatParser.java b/src/main/java/racingcar/view/util/FormatParser.java index e2429d5c5..b40eca686 100644 --- a/src/main/java/racingcar/view/util/FormatParser.java +++ b/src/main/java/racingcar/view/util/FormatParser.java @@ -25,6 +25,10 @@ public static String join(List values, String delimiter) { return joiner.toString(); } + public static String make(int count, String unit) { + return unit.repeat(count); + } + private static void validateEmpty(List values) { if (hasEmptyValue(values)) { throw new IllegalArgumentException(ErrorMessage.FORMAT_EMPTY); From 6de02f1a4c2db5024c0f80c8da1b23f03667d8fa Mon Sep 17 00:00:00 2001 From: yoondgu Date: Wed, 14 Dec 2022 22:36:44 +0900 Subject: [PATCH 15/26] =?UTF-8?q?feat:=20=EC=9E=90=EB=8F=99=EC=B0=A8=20?= =?UTF-8?q?=EA=B0=84=20=EC=9C=84=EC=B9=98=EB=A5=BC=20=EB=B9=84=EA=B5=90?= =?UTF-8?q?=ED=95=B4=20=EC=9A=B0=EC=8A=B9=EC=9E=90=EB=A5=BC=20=EA=B5=AC?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/README.md | 12 ++++++---- .../controller/RacingController.java | 6 +++++ src/main/java/racingcar/model/RacingGame.java | 16 +++++++++++++ src/main/java/racingcar/model/domain/Car.java | 8 +++++++ .../java/racingcar/model/RacingGameTest.java | 23 +++++++++++++++++++ 5 files changed, 61 insertions(+), 4 deletions(-) diff --git a/docs/README.md b/docs/README.md index 85901355e..30e6fa791 100644 --- a/docs/README.md +++ b/docs/README.md @@ -38,7 +38,7 @@ 3. - [x] 자동차가 전달받은 숫자에 따라 이동(전진하거나 전진하지않음)하는 기능 4. - - [ ] 자동차 간 위치를 비교해 가장 값이 큰, 최소 1명 최대 n명의 우승자를 구하는 기능 + - [x] 자동차 간 위치를 비교해 가장 값이 큰, 최소 1명 최대 n명의 우승자를 구하는 기능 5. - [x] 전달받은 회수 만큼 자동차 이동을 실행시키는 기능 @@ -84,9 +84,13 @@ ### 도메인 로직 단위테스트 -- [ ] 기능1 단위테스트 - - [ ] 실행 성공 - - [ ] 예외 테스트 +- [x] 자동차 등록 + - [x] 예외 테스트 +- [x] 자동차 이동 + - [x] 전진 + - [x] 정지 +- [x] 우승자 계산 + - [x] 성공 테스트 (공동 우승 확인) ## 🖋 클래스 설계 diff --git a/src/main/java/racingcar/controller/RacingController.java b/src/main/java/racingcar/controller/RacingController.java index 6e05c6924..232383097 100644 --- a/src/main/java/racingcar/controller/RacingController.java +++ b/src/main/java/racingcar/controller/RacingController.java @@ -15,6 +15,7 @@ public class RacingController { public void run() { enrollCarToRace(); moveCarsByCount(); + showWinner(); } public void enrollCarToRace() { @@ -27,4 +28,9 @@ public void moveCarsByCount() { List carStatuses = racingGame.repeatMovingCars(moveCount); outputView.printGameResult(carStatuses); } + + public void showWinner() { + List winnerNames = racingGame.findWinners(); + System.out.println(winnerNames); + } } diff --git a/src/main/java/racingcar/model/RacingGame.java b/src/main/java/racingcar/model/RacingGame.java index 05869c861..99642ece2 100644 --- a/src/main/java/racingcar/model/RacingGame.java +++ b/src/main/java/racingcar/model/RacingGame.java @@ -46,4 +46,20 @@ private List moveCars() { .map(Car::to) .collect(Collectors.toList()); } + + public List findWinners() { + List winnerNames = new ArrayList<>(); + int maxPosition = 0; + for (Car car : racingCars) { + maxPosition = Math.max(maxPosition, car.getPosition()); + } + findCarsAtPosition(maxPosition).forEach(car -> winnerNames.add(car.getName())); + return winnerNames; + } + + private List findCarsAtPosition(int position) { + return racingCars.stream() + .filter(car -> car.getPosition() == position) + .collect(Collectors.toList()); + } } diff --git a/src/main/java/racingcar/model/domain/Car.java b/src/main/java/racingcar/model/domain/Car.java index 3eea37c29..33ab2cea6 100644 --- a/src/main/java/racingcar/model/domain/Car.java +++ b/src/main/java/racingcar/model/domain/Car.java @@ -25,6 +25,14 @@ public void move(int number) { } } + public String getName() { + return name; + } + + public int getPosition() { + return position; + } + public CarDTO to() { return new CarDTO(name, position); } diff --git a/src/test/java/racingcar/model/RacingGameTest.java b/src/test/java/racingcar/model/RacingGameTest.java index fa273f83a..2763a9c63 100644 --- a/src/test/java/racingcar/model/RacingGameTest.java +++ b/src/test/java/racingcar/model/RacingGameTest.java @@ -1,8 +1,11 @@ package racingcar.model; +import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import java.util.LinkedList; import java.util.List; +import java.util.Queue; import org.junit.jupiter.api.Test; class RacingGameTest { @@ -28,6 +31,26 @@ class RacingGameTest { assertThatThrownBy(() -> racingGame.enrollCars(List.of("자동차명", "자동차명"))) .isInstanceOf(IllegalArgumentException.class); + } + + @Test + void 우승자_계산_성공() { + RacingGame racingGame = new RacingGame(new TestNumberGenerator()); + racingGame.enrollCars(List.of("pobi", "jason", "java", "joon")); + racingGame.repeatMovingCars(1); + + assertThat(racingGame.findWinners()).containsOnly("java", "joon"); + } + + private static class TestNumberGenerator implements NumberGenerator { + private final Queue numbers = new LinkedList<>(List.of(1, 3, 4, 4)); + @Override + public int make() { + if (numbers.isEmpty()) { + return -1; + } + return numbers.poll(); + } } } \ No newline at end of file From cfff773b37003fbd25c63d9d90126cbfacc00ee8 Mon Sep 17 00:00:00 2001 From: yoondgu Date: Wed, 14 Dec 2022 22:43:20 +0900 Subject: [PATCH 16/26] =?UTF-8?q?feat:=20=EC=9A=B0=EC=8A=B9=EC=9E=90=20?= =?UTF-8?q?=EC=95=88=EB=82=B4=20=EC=B6=9C=EB=A0=A5=ED=95=98=EB=8A=94=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/README.md | 8 ++++---- src/main/java/racingcar/controller/RacingController.java | 6 +++--- src/main/java/racingcar/view/OutputView.java | 4 ++++ src/main/java/racingcar/view/constants/Format.java | 1 + 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/docs/README.md b/docs/README.md index 30e6fa791..b6a7e2d43 100644 --- a/docs/README.md +++ b/docs/README.md @@ -54,7 +54,7 @@ 3. - [x] 차수 별 실행 결과 출력하는 기능 4. - - [ ] 우승자 안내 문구 출력하는 기능 + - [x] 우승자 안내 출력하는 기능 5. - [x] 이름 형식을 쉼표 구분 <-> 리스트로 파싱하는 기능 6. @@ -74,10 +74,10 @@ ### UI 로직 정보 - [x] 구분자 쉼표 -- [ ] 입력 안내 메시지 -- [ ] 에러 메시지 +- [x] 입력 안내 메시지 - [x] 실행 결과 출력 형식 -- [ ] 우승자 안내 문구 출력 형식 +- [x] 우승자 안내 문구 출력 형식 +- [ ] 에러 메시지 - [ ] 에러메시지 출력 형식 ## ✅ 단위테스트 목록 diff --git a/src/main/java/racingcar/controller/RacingController.java b/src/main/java/racingcar/controller/RacingController.java index 232383097..840346e49 100644 --- a/src/main/java/racingcar/controller/RacingController.java +++ b/src/main/java/racingcar/controller/RacingController.java @@ -15,7 +15,7 @@ public class RacingController { public void run() { enrollCarToRace(); moveCarsByCount(); - showWinner(); + showWinners(); } public void enrollCarToRace() { @@ -29,8 +29,8 @@ public void moveCarsByCount() { outputView.printGameResult(carStatuses); } - public void showWinner() { + public void showWinners() { List winnerNames = racingGame.findWinners(); - System.out.println(winnerNames); + outputView.printWinners(winnerNames); } } diff --git a/src/main/java/racingcar/view/OutputView.java b/src/main/java/racingcar/view/OutputView.java index 99a49f279..0bc9767f1 100644 --- a/src/main/java/racingcar/view/OutputView.java +++ b/src/main/java/racingcar/view/OutputView.java @@ -23,4 +23,8 @@ private void printCar(CarDTO carDTO) { System.out.printf(Format.OUTPUT_CAR_STATUS_FORMAT, carDTO.getName(), FormatParser.make(carDTO.getPosition(), Format.OUTPUT_CAR_POSITION_UNIT)); } + + public void printWinners(List winnerNames) { + System.out.printf(Format.OUTPUT_WINNERS_FORMAT, FormatParser.join(winnerNames, Format.OUTPUT_NAMES_DELIMITER)); + } } diff --git a/src/main/java/racingcar/view/constants/Format.java b/src/main/java/racingcar/view/constants/Format.java index d429e6a3a..c3f0e7ef4 100644 --- a/src/main/java/racingcar/view/constants/Format.java +++ b/src/main/java/racingcar/view/constants/Format.java @@ -5,4 +5,5 @@ public class Format { public static final String OUTPUT_NAMES_DELIMITER = ", "; public static final String OUTPUT_CAR_STATUS_FORMAT = "%s : %s" + System.lineSeparator(); public static final String OUTPUT_CAR_POSITION_UNIT = "-"; + public static final String OUTPUT_WINNERS_FORMAT = "최종 우승자 : %s" + System.lineSeparator(); } From f6de84150662d885db53babe255c72ca014a9359 Mon Sep 17 00:00:00 2001 From: yoondgu Date: Wed, 14 Dec 2022 22:45:05 +0900 Subject: [PATCH 17/26] =?UTF-8?q?feat:=20=EC=97=90=EB=9F=AC=20=EB=A9=94?= =?UTF-8?q?=EC=8B=9C=EC=A7=80=20=EC=B6=9C=EB=A0=A5=ED=95=98=EB=8A=94=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/README.md | 8 ++++---- src/main/java/racingcar/view/OutputView.java | 4 ++++ src/main/java/racingcar/view/constants/Format.java | 1 + 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/docs/README.md b/docs/README.md index b6a7e2d43..1c03fb938 100644 --- a/docs/README.md +++ b/docs/README.md @@ -58,7 +58,7 @@ 5. - [x] 이름 형식을 쉼표 구분 <-> 리스트로 파싱하는 기능 6. - - [ ] 에러 메시지 출력하는 기능 + - [x] 에러 메시지 출력하는 기능 7. - [ ] 잘못된 값 입력 시 그 부분부터 재입력하는 기능 @@ -69,7 +69,7 @@ - [x] 이동 숫자 0 ~ 9, 전진 조건 4 - [x] 이름 길이 상한 제한 5 - [x] 자동차 개수 하한 제한 2 -- [ ] 에러 메시지 +- [x] 에러 메시지 ### UI 로직 정보 @@ -77,8 +77,8 @@ - [x] 입력 안내 메시지 - [x] 실행 결과 출력 형식 - [x] 우승자 안내 문구 출력 형식 -- [ ] 에러 메시지 -- [ ] 에러메시지 출력 형식 +- [x] 에러 메시지 +- [x] 에러메시지 출력 형식 ## ✅ 단위테스트 목록 diff --git a/src/main/java/racingcar/view/OutputView.java b/src/main/java/racingcar/view/OutputView.java index 0bc9767f1..fa7bfec51 100644 --- a/src/main/java/racingcar/view/OutputView.java +++ b/src/main/java/racingcar/view/OutputView.java @@ -27,4 +27,8 @@ private void printCar(CarDTO carDTO) { public void printWinners(List winnerNames) { System.out.printf(Format.OUTPUT_WINNERS_FORMAT, FormatParser.join(winnerNames, Format.OUTPUT_NAMES_DELIMITER)); } + + public void printErrorMessage(String message) { + System.out.printf(Format.OUTPUT_ERROR_FORMAT, message); + } } diff --git a/src/main/java/racingcar/view/constants/Format.java b/src/main/java/racingcar/view/constants/Format.java index c3f0e7ef4..59fb1682f 100644 --- a/src/main/java/racingcar/view/constants/Format.java +++ b/src/main/java/racingcar/view/constants/Format.java @@ -6,4 +6,5 @@ public class Format { public static final String OUTPUT_CAR_STATUS_FORMAT = "%s : %s" + System.lineSeparator(); public static final String OUTPUT_CAR_POSITION_UNIT = "-"; public static final String OUTPUT_WINNERS_FORMAT = "최종 우승자 : %s" + System.lineSeparator(); + public static final String OUTPUT_ERROR_FORMAT = "[ERROR] %s" + System.lineSeparator(); } From 1d001a56cb9e3d0b76a0e5f6d221c30dd4484144 Mon Sep 17 00:00:00 2001 From: yoondgu Date: Wed, 14 Dec 2022 22:50:53 +0900 Subject: [PATCH 18/26] =?UTF-8?q?feat:=20=EC=9E=98=EB=AA=BB=EB=90=9C=20?= =?UTF-8?q?=EA=B0=92=20=EC=9E=85=EB=A0=A5=20=EC=8B=9C=20=EA=B7=B8=20?= =?UTF-8?q?=EB=B6=80=EB=B6=84=EB=B6=80=ED=84=B0=20=EC=9E=AC=EC=9E=85?= =?UTF-8?q?=EB=A0=A5=ED=95=98=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/README.md | 2 +- .../racingcar/controller/RacingController.java | 17 +++++++++++------ .../controller/util/ExceptionHandler.java | 16 ++++++++++++++++ 3 files changed, 28 insertions(+), 7 deletions(-) create mode 100644 src/main/java/racingcar/controller/util/ExceptionHandler.java diff --git a/docs/README.md b/docs/README.md index 1c03fb938..467695358 100644 --- a/docs/README.md +++ b/docs/README.md @@ -60,7 +60,7 @@ 6. - [x] 에러 메시지 출력하는 기능 7. - - [ ] 잘못된 값 입력 시 그 부분부터 재입력하는 기능 + - [x] 잘못된 값 입력 시 그 부분부터 재입력하는 기능 ## 🗄 상수값 목록 diff --git a/src/main/java/racingcar/controller/RacingController.java b/src/main/java/racingcar/controller/RacingController.java index 840346e49..d372343fe 100644 --- a/src/main/java/racingcar/controller/RacingController.java +++ b/src/main/java/racingcar/controller/RacingController.java @@ -1,6 +1,7 @@ package racingcar.controller; import java.util.List; +import racingcar.controller.util.ExceptionHandler; import racingcar.dto.CarStatusDTO; import racingcar.model.RacingGame; import racingcar.model.RandomNumberGenerator; @@ -13,23 +14,27 @@ public class RacingController { private final RacingGame racingGame = new RacingGame(new RandomNumberGenerator()); public void run() { - enrollCarToRace(); - moveCarsByCount(); - showWinners(); + executeFunction(this::enrollCarToRace); + executeFunction(this::moveCarsByCount); + executeFunction(this::showWinners); } - public void enrollCarToRace() { + private void executeFunction(Runnable runnable) { + ExceptionHandler.retryForIllegalArgument(runnable, outputView::printErrorMessage); + } + + private void enrollCarToRace() { List carNames = inputView.inputCarNames(); racingGame.enrollCars(carNames); } - public void moveCarsByCount() { + private void moveCarsByCount() { int moveCount = inputView.inputMoveCount(); List carStatuses = racingGame.repeatMovingCars(moveCount); outputView.printGameResult(carStatuses); } - public void showWinners() { + private void showWinners() { List winnerNames = racingGame.findWinners(); outputView.printWinners(winnerNames); } diff --git a/src/main/java/racingcar/controller/util/ExceptionHandler.java b/src/main/java/racingcar/controller/util/ExceptionHandler.java new file mode 100644 index 000000000..cf74bb451 --- /dev/null +++ b/src/main/java/racingcar/controller/util/ExceptionHandler.java @@ -0,0 +1,16 @@ +package racingcar.controller.util; + +import java.util.function.Consumer; + +public class ExceptionHandler { + public static void retryForIllegalArgument(Runnable runnable, Consumer exceptionMessageHandling) { + while (true) { + try { + runnable.run(); + return; + } catch (IllegalArgumentException exception) { + exceptionMessageHandling.accept(exception.getMessage()); + } + } + } +} From 13bce6f8263b643a19932b3404f4a173afd87cc1 Mon Sep 17 00:00:00 2001 From: yoondgu Date: Wed, 14 Dec 2022 22:54:18 +0900 Subject: [PATCH 19/26] =?UTF-8?q?refactor:=20=EC=A0=95=EC=A0=81=20?= =?UTF-8?q?=EC=83=81=EC=88=98,=20=EB=A9=94=EC=86=8C=EB=93=9C=EB=A7=8C=20?= =?UTF-8?q?=EA=B0=80=EC=A7=84=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EA=B0=9D?= =?UTF-8?q?=EC=B2=B4=20=EC=83=9D=EC=84=B1=20=EB=B0=A9=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/racingcar/controller/util/ExceptionHandler.java | 3 +++ src/main/java/racingcar/model/constants/ErrorMessage.java | 3 +++ src/main/java/racingcar/model/constants/GameRule.java | 3 +++ src/main/java/racingcar/view/constants/ErrorMessage.java | 3 +++ src/main/java/racingcar/view/constants/Format.java | 3 +++ src/main/java/racingcar/view/constants/InputMessage.java | 3 +++ src/main/java/racingcar/view/constants/OutputMessage.java | 3 +++ src/main/java/racingcar/view/util/NumberParser.java | 3 +++ 8 files changed, 24 insertions(+) diff --git a/src/main/java/racingcar/controller/util/ExceptionHandler.java b/src/main/java/racingcar/controller/util/ExceptionHandler.java index cf74bb451..9e9a0092d 100644 --- a/src/main/java/racingcar/controller/util/ExceptionHandler.java +++ b/src/main/java/racingcar/controller/util/ExceptionHandler.java @@ -3,6 +3,9 @@ import java.util.function.Consumer; public class ExceptionHandler { + private ExceptionHandler() { + } + public static void retryForIllegalArgument(Runnable runnable, Consumer exceptionMessageHandling) { while (true) { try { diff --git a/src/main/java/racingcar/model/constants/ErrorMessage.java b/src/main/java/racingcar/model/constants/ErrorMessage.java index 4b10f4c8d..c3099892c 100644 --- a/src/main/java/racingcar/model/constants/ErrorMessage.java +++ b/src/main/java/racingcar/model/constants/ErrorMessage.java @@ -4,4 +4,7 @@ public class ErrorMessage { public static final String CARS_LACK_OF_COUNT = "경주할 자동차는 최소 2대 필요합니다."; public static final String CAR_NAME_LENGTH_OVER = "자동차 이름은 최대 5글자까지만 가능합니다."; public static final String CARS_DUPLICATED_NAME = "중복된 자동차 이름을 등록할 수 없습니다."; + + private ErrorMessage() { + } } diff --git a/src/main/java/racingcar/model/constants/GameRule.java b/src/main/java/racingcar/model/constants/GameRule.java index a5dfe5c3b..b25c8b963 100644 --- a/src/main/java/racingcar/model/constants/GameRule.java +++ b/src/main/java/racingcar/model/constants/GameRule.java @@ -4,4 +4,7 @@ public class GameRule { public static final int NAME_LENGTH_UPPER_LIMIT = 5; public static final int CARS_COUNT_LOWER_LIMIT = 2; public static final int CAR_FORWARD_LOWER_LIMIT = 4; + + private GameRule() { + } } diff --git a/src/main/java/racingcar/view/constants/ErrorMessage.java b/src/main/java/racingcar/view/constants/ErrorMessage.java index e77f4f23b..50f6badd8 100644 --- a/src/main/java/racingcar/view/constants/ErrorMessage.java +++ b/src/main/java/racingcar/view/constants/ErrorMessage.java @@ -5,4 +5,7 @@ public class ErrorMessage { public static final String FORMAT_INVALID = "입력값의 형식이 잘못되었습니다."; public static final String NOT_A_DIGIT = "입력값이 자연수가 아닙니다."; public static final String NOT_A_NUMBER = "입력값이 숫자가 아닙니다."; + + private ErrorMessage() { + } } diff --git a/src/main/java/racingcar/view/constants/Format.java b/src/main/java/racingcar/view/constants/Format.java index 59fb1682f..3810c1ec9 100644 --- a/src/main/java/racingcar/view/constants/Format.java +++ b/src/main/java/racingcar/view/constants/Format.java @@ -7,4 +7,7 @@ public class Format { public static final String OUTPUT_CAR_POSITION_UNIT = "-"; public static final String OUTPUT_WINNERS_FORMAT = "최종 우승자 : %s" + System.lineSeparator(); public static final String OUTPUT_ERROR_FORMAT = "[ERROR] %s" + System.lineSeparator(); + + private Format() { + } } diff --git a/src/main/java/racingcar/view/constants/InputMessage.java b/src/main/java/racingcar/view/constants/InputMessage.java index ce09f2526..506c82782 100644 --- a/src/main/java/racingcar/view/constants/InputMessage.java +++ b/src/main/java/racingcar/view/constants/InputMessage.java @@ -3,4 +3,7 @@ public class InputMessage { public static final String CAR_NAMES = "경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)"; public static final String MOVE_COUNT = "시도할 회수는 몇회인가요?"; + + private InputMessage() { + } } diff --git a/src/main/java/racingcar/view/constants/OutputMessage.java b/src/main/java/racingcar/view/constants/OutputMessage.java index 6ec77ea7d..34b8660c3 100644 --- a/src/main/java/racingcar/view/constants/OutputMessage.java +++ b/src/main/java/racingcar/view/constants/OutputMessage.java @@ -2,4 +2,7 @@ public class OutputMessage { public static final String GAME_RESULT = "실행 결과"; + + private OutputMessage() { + } } diff --git a/src/main/java/racingcar/view/util/NumberParser.java b/src/main/java/racingcar/view/util/NumberParser.java index 783dcf545..3ae9bc410 100644 --- a/src/main/java/racingcar/view/util/NumberParser.java +++ b/src/main/java/racingcar/view/util/NumberParser.java @@ -3,6 +3,9 @@ import racingcar.view.constants.ErrorMessage; public class NumberParser { + private NumberParser() { + } + public static int parseDigit(String value) { int number = parseInteger(value); if (number <= 0) { From 70a940221e6982be80dfba56bc714430b297b305 Mon Sep 17 00:00:00 2001 From: yoondgu Date: Wed, 14 Dec 2022 22:59:27 +0900 Subject: [PATCH 20/26] =?UTF-8?q?refactor:=20=EC=9E=85=EB=A0=A5=20?= =?UTF-8?q?=ED=98=95=EC=8B=9D=20=EA=B2=80=EC=A6=9D=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=ED=8C=8C=EC=8B=B1=ED=81=B4=EB=9E=98=EC=8A=A4=EB=A1=9C=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/README.md | 2 +- src/main/java/racingcar/view/InputView.java | 15 ---------- .../racingcar/view/util/FormatParser.java | 28 +++++++++++++++---- 3 files changed, 23 insertions(+), 22 deletions(-) diff --git a/docs/README.md b/docs/README.md index 467695358..450165831 100644 --- a/docs/README.md +++ b/docs/README.md @@ -101,7 +101,7 @@ ### 코드 스타일 및 요구사항 - [ ] 인덴트 2 이하 검토 -- [ ] 메소드 분리 검토 +- [ ] 메소드, 클래스 분리 검토 - [ ] 메소드 길이 10라인 이하 검토 - [ ] 파라미터 개수 3개 이하 검토 - [ ] 변수, 메소드 선언 순서 정리 (메소드 정렬 기준 1순위: 메소드 간 논리적 연관성, 2순위: public-private) diff --git a/src/main/java/racingcar/view/InputView.java b/src/main/java/racingcar/view/InputView.java index 16d060957..4beb28fc3 100644 --- a/src/main/java/racingcar/view/InputView.java +++ b/src/main/java/racingcar/view/InputView.java @@ -4,7 +4,6 @@ import camp.nextstep.edu.missionutils.Console; import java.util.List; -import racingcar.view.constants.ErrorMessage; import racingcar.view.constants.InputMessage; import racingcar.view.util.FormatParser; import racingcar.view.util.NumberParser; @@ -13,23 +12,9 @@ public class InputView { public List inputCarNames() { System.out.println(InputMessage.CAR_NAMES); String line = Console.readLine(); - validateEmpty(line); return FormatParser.split(line, INPUT_NAMES_DELIMITER); } - private void validateEmpty(String line) { - if (hasWrongFormat(line)) { - throw new IllegalArgumentException(ErrorMessage.FORMAT_INVALID); - } - } - - private boolean hasWrongFormat(String line) { - if (line.isEmpty()) { - return true; - } - return line.startsWith(INPUT_NAMES_DELIMITER) || line.endsWith(INPUT_NAMES_DELIMITER); - } - public int inputMoveCount() { System.out.println(InputMessage.MOVE_COUNT); String line = Console.readLine(); diff --git a/src/main/java/racingcar/view/util/FormatParser.java b/src/main/java/racingcar/view/util/FormatParser.java index b40eca686..7a28aaf66 100644 --- a/src/main/java/racingcar/view/util/FormatParser.java +++ b/src/main/java/racingcar/view/util/FormatParser.java @@ -1,5 +1,7 @@ package racingcar.view.util; +import static racingcar.view.constants.Format.INPUT_NAMES_DELIMITER; + import java.util.Arrays; import java.util.List; import java.util.StringJoiner; @@ -11,6 +13,7 @@ private FormatParser() { } public static List split(String line, String delimiter) { + validateWrongFormat(line); String[] values = line.split(delimiter); List parsedValue = Arrays.stream(values) .map(String::trim) @@ -19,14 +22,17 @@ public static List split(String line, String delimiter) { return parsedValue; } - public static String join(List values, String delimiter) { - StringJoiner joiner = new StringJoiner(delimiter); - values.forEach(joiner::add); - return joiner.toString(); + private static void validateWrongFormat(String line) { + if (hasWrongFormat(line)) { + throw new IllegalArgumentException(ErrorMessage.FORMAT_INVALID); + } } - public static String make(int count, String unit) { - return unit.repeat(count); + private static boolean hasWrongFormat(String line) { + if (line.isEmpty()) { + return true; + } + return line.startsWith(INPUT_NAMES_DELIMITER) || line.endsWith(INPUT_NAMES_DELIMITER); } private static void validateEmpty(List values) { @@ -39,4 +45,14 @@ private static boolean hasEmptyValue(List values) { return values.stream() .anyMatch(String::isEmpty); } + + public static String join(List values, String delimiter) { + StringJoiner joiner = new StringJoiner(delimiter); + values.forEach(joiner::add); + return joiner.toString(); + } + + public static String make(int count, String unit) { + return unit.repeat(count); + } } From d23576201fa752ebbf6c7acecdddfbfcbb40adf0 Mon Sep 17 00:00:00 2001 From: yoondgu Date: Wed, 14 Dec 2022 23:07:47 +0900 Subject: [PATCH 21/26] =?UTF-8?q?refactor:=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=8A=A4=ED=83=80=EC=9D=BC=20=EC=9A=94=EA=B5=AC=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20=EA=B2=80=ED=86=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/README.md | 17 ++++++++--------- .../racingcar/controller/RacingController.java | 8 ++++---- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/docs/README.md b/docs/README.md index 450165831..59dcac7f3 100644 --- a/docs/README.md +++ b/docs/README.md @@ -100,12 +100,11 @@ ### 코드 스타일 및 요구사항 -- [ ] 인덴트 2 이하 검토 -- [ ] 메소드, 클래스 분리 검토 -- [ ] 메소드 길이 10라인 이하 검토 -- [ ] 파라미터 개수 3개 이하 검토 -- [ ] 변수, 메소드 선언 순서 정리 (메소드 정렬 기준 1순위: 메소드 간 논리적 연관성, 2순위: public-private) -- [ ] Java 코드 컨벤션 가이드 준수 검토 -- [ ] 요구사항에 주어진 클래스 적용 규칙 검토 -- [ ] TODO 주석 모두 해결 후 삭제 -- [ ] 클래스 주석 작성 \ No newline at end of file +- [x] 인덴트 2 이하 검토 +- [ ] 메소드, 클래스 분리 검토 : RacingGame의 cars 관련 메소드 분리 검토 +- [x] 메소드 길이 10라인 이하 검토 +- [x] 파라미터 개수 3개 이하 검토 +- [x] 변수, 메소드 선언 순서 정리 (메소드 정렬 기준 1순위: 메소드 간 논리적 연관성, 2순위: public-private) +- [x] Java 코드 컨벤션 가이드 준수 검토 +- [x] 요구사항에 주어진 클래스 적용 규칙 검토 +- [x] TODO 주석 모두 해결 후 삭제 \ No newline at end of file diff --git a/src/main/java/racingcar/controller/RacingController.java b/src/main/java/racingcar/controller/RacingController.java index d372343fe..057512d5f 100644 --- a/src/main/java/racingcar/controller/RacingController.java +++ b/src/main/java/racingcar/controller/RacingController.java @@ -14,12 +14,12 @@ public class RacingController { private final RacingGame racingGame = new RacingGame(new RandomNumberGenerator()); public void run() { - executeFunction(this::enrollCarToRace); - executeFunction(this::moveCarsByCount); - executeFunction(this::showWinners); + repeatUntilGetLegalAnswer(this::enrollCarToRace); + repeatUntilGetLegalAnswer(this::moveCarsByCount); + showWinners(); } - private void executeFunction(Runnable runnable) { + private void repeatUntilGetLegalAnswer(Runnable runnable) { ExceptionHandler.retryForIllegalArgument(runnable, outputView::printErrorMessage); } From cd1688602e069f9990541d263fb545418e65f4ec Mon Sep 17 00:00:00 2001 From: yoondgu Date: Wed, 14 Dec 2022 23:17:40 +0900 Subject: [PATCH 22/26] =?UTF-8?q?refactor:=20racingCars=20=EC=9D=BC?= =?UTF-8?q?=EA=B8=89=20=EC=BD=9C=EB=A0=89=EC=85=98=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/README.md | 2 +- src/main/java/racingcar/model/RacingGame.java | 33 ++++----------- .../racingcar/model/domain/RacingCars.java | 42 +++++++++++++++++++ .../java/racingcar/model/RacingGameTest.java | 27 +----------- .../model/domain/RacingCarsTest.java | 32 ++++++++++++++ 5 files changed, 84 insertions(+), 52 deletions(-) create mode 100644 src/main/java/racingcar/model/domain/RacingCars.java create mode 100644 src/test/java/racingcar/model/domain/RacingCarsTest.java diff --git a/docs/README.md b/docs/README.md index 59dcac7f3..141c6f868 100644 --- a/docs/README.md +++ b/docs/README.md @@ -101,7 +101,7 @@ ### 코드 스타일 및 요구사항 - [x] 인덴트 2 이하 검토 -- [ ] 메소드, 클래스 분리 검토 : RacingGame의 cars 관련 메소드 분리 검토 +- [x] 메소드, 클래스 분리 검토 - [x] 메소드 길이 10라인 이하 검토 - [x] 파라미터 개수 3개 이하 검토 - [x] 변수, 메소드 선언 순서 정리 (메소드 정렬 기준 1순위: 메소드 간 논리적 연관성, 2순위: public-private) diff --git a/src/main/java/racingcar/model/RacingGame.java b/src/main/java/racingcar/model/RacingGame.java index 99642ece2..3b21f3d4c 100644 --- a/src/main/java/racingcar/model/RacingGame.java +++ b/src/main/java/racingcar/model/RacingGame.java @@ -1,17 +1,15 @@ package racingcar.model; import java.util.ArrayList; -import java.util.HashSet; import java.util.List; import java.util.stream.Collectors; import racingcar.dto.CarDTO; import racingcar.dto.CarStatusDTO; -import racingcar.model.constants.ErrorMessage; -import racingcar.model.constants.GameRule; import racingcar.model.domain.Car; +import racingcar.model.domain.RacingCars; public class RacingGame { - private final List racingCars = new ArrayList<>(); + private final RacingCars racingCars = new RacingCars(); private final NumberGenerator numberGenerator; public RacingGame(NumberGenerator numberGenerator) { @@ -19,17 +17,7 @@ public RacingGame(NumberGenerator numberGenerator) { } public void enrollCars(List carNames) { - validateCarNames(carNames); - carNames.forEach(name -> racingCars.add(new Car(name))); - } - - private void validateCarNames(List carNames) { - if (carNames.size() < GameRule.CARS_COUNT_LOWER_LIMIT) { - throw new IllegalArgumentException(ErrorMessage.CARS_LACK_OF_COUNT); - } - if (carNames.size() != new HashSet<>(carNames).size()) { - throw new IllegalArgumentException(ErrorMessage.CARS_DUPLICATED_NAME); - } + racingCars.addCars(carNames); } public List repeatMovingCars(int moveCount) { @@ -41,8 +29,9 @@ public List repeatMovingCars(int moveCount) { } private List moveCars() { - racingCars.forEach(car -> car.move(numberGenerator.make())); - return racingCars.stream() + racingCars.race(numberGenerator); + List cars = racingCars.cars(); + return cars.stream() .map(Car::to) .collect(Collectors.toList()); } @@ -50,16 +39,10 @@ private List moveCars() { public List findWinners() { List winnerNames = new ArrayList<>(); int maxPosition = 0; - for (Car car : racingCars) { + for (Car car : racingCars.cars()) { maxPosition = Math.max(maxPosition, car.getPosition()); } - findCarsAtPosition(maxPosition).forEach(car -> winnerNames.add(car.getName())); + racingCars.findCarsAtPosition(maxPosition).forEach(car -> winnerNames.add(car.getName())); return winnerNames; } - - private List findCarsAtPosition(int position) { - return racingCars.stream() - .filter(car -> car.getPosition() == position) - .collect(Collectors.toList()); - } } diff --git a/src/main/java/racingcar/model/domain/RacingCars.java b/src/main/java/racingcar/model/domain/RacingCars.java new file mode 100644 index 000000000..d77bfe408 --- /dev/null +++ b/src/main/java/racingcar/model/domain/RacingCars.java @@ -0,0 +1,42 @@ +package racingcar.model.domain; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.stream.Collectors; +import racingcar.model.NumberGenerator; +import racingcar.model.constants.ErrorMessage; +import racingcar.model.constants.GameRule; + +public class RacingCars { + private final List cars = new ArrayList<>(); + + private void validateCarNames(List carNames) { + if (carNames.size() < GameRule.CARS_COUNT_LOWER_LIMIT) { + throw new IllegalArgumentException(ErrorMessage.CARS_LACK_OF_COUNT); + } + if (carNames.size() != new HashSet<>(carNames).size()) { + throw new IllegalArgumentException(ErrorMessage.CARS_DUPLICATED_NAME); + } + } + + public void addCars(List carNames) { + validateCarNames(carNames); + carNames.forEach(name -> cars.add(new Car(name))); + } + + public void race(NumberGenerator numberGenerator) { + cars.forEach(car -> car.move(numberGenerator.make())); + } + + public List cars() { + return Collections.unmodifiableList(cars); + } + + public List findCarsAtPosition(int position) { + return cars.stream() + .filter(car -> car.getPosition() == position) + .collect(Collectors.toList()); + } +} diff --git a/src/test/java/racingcar/model/RacingGameTest.java b/src/test/java/racingcar/model/RacingGameTest.java index 2763a9c63..d17e85b19 100644 --- a/src/test/java/racingcar/model/RacingGameTest.java +++ b/src/test/java/racingcar/model/RacingGameTest.java @@ -1,7 +1,6 @@ package racingcar.model; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; import java.util.LinkedList; import java.util.List; @@ -9,36 +8,12 @@ import org.junit.jupiter.api.Test; class RacingGameTest { - @Test - void 자동차_등록_개수_예외발생() { - RacingGame racingGame = new RacingGame(new RandomNumberGenerator()); - - assertThatThrownBy(() -> racingGame.enrollCars(List.of("자동차명"))) - .isInstanceOf(IllegalArgumentException.class); - } - - @Test - void 자동차_등록_이름길이_예외발생() { - RacingGame racingGame = new RacingGame(new RandomNumberGenerator()); - - assertThatThrownBy(() -> racingGame.enrollCars(List.of("자동차명여섯"))) - .isInstanceOf(IllegalArgumentException.class); - } - - @Test - void 자동차_등록_중복이름_예외발생() { - RacingGame racingGame = new RacingGame(new RandomNumberGenerator()); - - assertThatThrownBy(() -> racingGame.enrollCars(List.of("자동차명", "자동차명"))) - .isInstanceOf(IllegalArgumentException.class); - } - @Test void 우승자_계산_성공() { RacingGame racingGame = new RacingGame(new TestNumberGenerator()); racingGame.enrollCars(List.of("pobi", "jason", "java", "joon")); racingGame.repeatMovingCars(1); - + assertThat(racingGame.findWinners()).containsOnly("java", "joon"); } diff --git a/src/test/java/racingcar/model/domain/RacingCarsTest.java b/src/test/java/racingcar/model/domain/RacingCarsTest.java new file mode 100644 index 000000000..c9bf538a0 --- /dev/null +++ b/src/test/java/racingcar/model/domain/RacingCarsTest.java @@ -0,0 +1,32 @@ +package racingcar.model.domain; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.util.List; +import org.junit.jupiter.api.Test; + +class RacingCarsTest { + @Test + void 자동차_등록_개수_예외발생() { + RacingCars racingCars = new RacingCars(); + + assertThatThrownBy(() -> racingCars.addCars(List.of("자동차명"))) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + void 자동차_등록_이름길이_예외발생() { + RacingCars racingCars = new RacingCars(); + + assertThatThrownBy(() -> racingCars.addCars(List.of("자동차명여섯"))) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + void 자동차_등록_중복이름_예외발생() { + RacingCars racingCars = new RacingCars(); + + assertThatThrownBy(() -> racingCars.addCars(List.of("자동차명", "자동차명"))) + .isInstanceOf(IllegalArgumentException.class); + } +} \ No newline at end of file From 8ba1b459132a63ec435efbbf0c74f5905d87ec07 Mon Sep 17 00:00:00 2001 From: yoondgu Date: Wed, 14 Dec 2022 23:28:26 +0900 Subject: [PATCH 23/26] =?UTF-8?q?test:=20=EC=9E=90=EB=8F=99=EC=B0=A8=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99=20=EA=B8=B0=EB=8A=A5=20=EC=98=88=EC=99=B8?= =?UTF-8?q?=EC=82=AC=ED=95=AD=20=EC=B6=94=EA=B0=80,=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/README.md | 4 +++- .../java/racingcar/model/RandomNumberGenerator.java | 8 ++++---- .../java/racingcar/model/constants/ErrorMessage.java | 3 ++- .../java/racingcar/model/constants/GameRule.java | 3 +++ src/main/java/racingcar/model/domain/Car.java | 7 +++++++ src/test/java/racingcar/model/domain/CarTest.java | 12 ++++++++++++ 6 files changed, 31 insertions(+), 6 deletions(-) diff --git a/docs/README.md b/docs/README.md index 141c6f868..8d4dad99b 100644 --- a/docs/README.md +++ b/docs/README.md @@ -32,11 +32,12 @@ - [x] 경주할 모든 자동차의 이름을 저장하는 기능 - [x] `예외발생` : 경주할 자동차가 1대 - [x] `예외발생` : 이름의 길이가 5자 초과 - - [x] `예외발생` : 중복 이름이 존재하는 경우 + - [x] `예외발생` : 중복 이름 존재 2. - [x] 랜덤 숫자를 생성하는 기능 3. - [x] 자동차가 전달받은 숫자에 따라 이동(전진하거나 전진하지않음)하는 기능 + - [x] `예외발생` : 범위 밖 숫자 4. - [x] 자동차 간 위치를 비교해 가장 값이 큰, 최소 1명 최대 n명의 우승자를 구하는 기능 5. @@ -89,6 +90,7 @@ - [x] 자동차 이동 - [x] 전진 - [x] 정지 + - [x] 예외 테스트 - [x] 우승자 계산 - [x] 성공 테스트 (공동 우승 확인) diff --git a/src/main/java/racingcar/model/RandomNumberGenerator.java b/src/main/java/racingcar/model/RandomNumberGenerator.java index 96c280c92..14ba6381b 100644 --- a/src/main/java/racingcar/model/RandomNumberGenerator.java +++ b/src/main/java/racingcar/model/RandomNumberGenerator.java @@ -1,13 +1,13 @@ package racingcar.model; +import static racingcar.model.constants.GameRule.NUMBER_RANGE_END; +import static racingcar.model.constants.GameRule.NUMBER_RANGE_START; + import camp.nextstep.edu.missionutils.Randoms; public class RandomNumberGenerator implements NumberGenerator { - private static final int RANGE_START = 0; - private static final int RANGE_END = 9; - @Override public int make() { - return Randoms.pickNumberInRange(RANGE_START, RANGE_END); + return Randoms.pickNumberInRange(NUMBER_RANGE_START, NUMBER_RANGE_END); } } diff --git a/src/main/java/racingcar/model/constants/ErrorMessage.java b/src/main/java/racingcar/model/constants/ErrorMessage.java index c3099892c..81b39b4e4 100644 --- a/src/main/java/racingcar/model/constants/ErrorMessage.java +++ b/src/main/java/racingcar/model/constants/ErrorMessage.java @@ -4,7 +4,8 @@ public class ErrorMessage { public static final String CARS_LACK_OF_COUNT = "경주할 자동차는 최소 2대 필요합니다."; public static final String CAR_NAME_LENGTH_OVER = "자동차 이름은 최대 5글자까지만 가능합니다."; public static final String CARS_DUPLICATED_NAME = "중복된 자동차 이름을 등록할 수 없습니다."; - + public static final String MOVING_NUMBER_OUT_OF_BOUNDS = "자동차 이동을 위한 값이 범위 밖 수입니다."; + private ErrorMessage() { } } diff --git a/src/main/java/racingcar/model/constants/GameRule.java b/src/main/java/racingcar/model/constants/GameRule.java index b25c8b963..785b7bae9 100644 --- a/src/main/java/racingcar/model/constants/GameRule.java +++ b/src/main/java/racingcar/model/constants/GameRule.java @@ -4,6 +4,9 @@ public class GameRule { public static final int NAME_LENGTH_UPPER_LIMIT = 5; public static final int CARS_COUNT_LOWER_LIMIT = 2; public static final int CAR_FORWARD_LOWER_LIMIT = 4; + public static final int NUMBER_RANGE_START = 0; + public static final int NUMBER_RANGE_END = 9; + private GameRule() { } diff --git a/src/main/java/racingcar/model/domain/Car.java b/src/main/java/racingcar/model/domain/Car.java index 33ab2cea6..cf29865d4 100644 --- a/src/main/java/racingcar/model/domain/Car.java +++ b/src/main/java/racingcar/model/domain/Car.java @@ -20,11 +20,18 @@ private void validateName(String name) { } public void move(int number) { + validateNumber(number); if (number >= GameRule.CAR_FORWARD_LOWER_LIMIT) { position++; } } + private void validateNumber(int number) { + if (number < GameRule.NUMBER_RANGE_START || number > GameRule.NUMBER_RANGE_END) { + throw new IllegalArgumentException(ErrorMessage.MOVING_NUMBER_OUT_OF_BOUNDS); + } + } + public String getName() { return name; } diff --git a/src/test/java/racingcar/model/domain/CarTest.java b/src/test/java/racingcar/model/domain/CarTest.java index bf8f47960..513483a72 100644 --- a/src/test/java/racingcar/model/domain/CarTest.java +++ b/src/test/java/racingcar/model/domain/CarTest.java @@ -1,8 +1,11 @@ package racingcar.model.domain; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; class CarTest { @Test @@ -20,4 +23,13 @@ class CarTest { assertThat(car.to().getPosition()).isEqualTo(0); } + + @ParameterizedTest + @ValueSource(ints = {-1, 10}) + void 이동_범위_밖_수_예외발생(int number) { + Car car = new Car("테스트"); + + assertThatThrownBy(() -> car.move(number)) + .isInstanceOf(IllegalArgumentException.class); + } } \ No newline at end of file From ed509c80da806126788eaa549890e767a2b6c17c Mon Sep 17 00:00:00 2001 From: yoondgu Date: Wed, 14 Dec 2022 23:32:10 +0900 Subject: [PATCH 24/26] =?UTF-8?q?docs:=20=EB=AF=B8=EC=85=98=20=EB=AA=85?= =?UTF-8?q?=EC=84=B8=EC=84=9C=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EA=B5=AC?= =?UTF-8?q?=EC=A1=B0=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/README.md | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/docs/README.md b/docs/README.md index 8d4dad99b..fe5d80267 100644 --- a/docs/README.md +++ b/docs/README.md @@ -96,7 +96,38 @@ ## 🖋 클래스 설계 -[작성 예정] +``` +└── racingcar + ├── Application.java + ├── controller + │ ├── RacingController.java + │ └── util + │ └── ExceptionHandler.java + ├── dto + │ ├── CarDTO.java + │ └── CarStatusDTO.java + ├── model + │ ├── NumberGenerator.java + │ ├── RacingGame.java + │ ├── RandomNumberGenerator.java + │ ├── constants + │ │ ├── ErrorMessage.java + │ │ └── GameRule.java + │ └── domain + │ ├── Car.java + │ └── RacingCars.java + └── view + ├── InputView.java + ├── OutputView.java + ├── constants + │ ├── ErrorMessage.java + │ ├── Format.java + │ ├── InputMessage.java + │ └── OutputMessage.java + └── util + ├── FormatParser.java + └── NumberParser.java +``` ## ♻️ 리팩토링 검토 목록 From 57d8c46515044caede34726e46e2d7a03d96c202 Mon Sep 17 00:00:00 2001 From: yoondgu Date: Thu, 15 Dec 2022 10:13:57 +0900 Subject: [PATCH 25/26] =?UTF-8?q?refactor:=20=EC=9E=90=EB=8F=99=EC=B0=A8?= =?UTF-8?q?=20=EC=9C=84=EC=B9=98=20=EB=B9=84=EA=B5=90=20getter=20=EB=A9=94?= =?UTF-8?q?=EC=86=8C=EB=93=9C=20=EC=82=AD=EC=A0=9C,=20compareTo=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/racingcar/model/RacingGame.java | 8 +------- src/main/java/racingcar/model/domain/Car.java | 11 ++++++----- .../racingcar/model/domain/RacingCars.java | 19 ++++++++++++++----- .../racingcar/view/util/FormatParser.java | 5 +---- 4 files changed, 22 insertions(+), 21 deletions(-) diff --git a/src/main/java/racingcar/model/RacingGame.java b/src/main/java/racingcar/model/RacingGame.java index 3b21f3d4c..8d60d9447 100644 --- a/src/main/java/racingcar/model/RacingGame.java +++ b/src/main/java/racingcar/model/RacingGame.java @@ -37,12 +37,6 @@ private List moveCars() { } public List findWinners() { - List winnerNames = new ArrayList<>(); - int maxPosition = 0; - for (Car car : racingCars.cars()) { - maxPosition = Math.max(maxPosition, car.getPosition()); - } - racingCars.findCarsAtPosition(maxPosition).forEach(car -> winnerNames.add(car.getName())); - return winnerNames; + return racingCars.findWinnerNames(); } } diff --git a/src/main/java/racingcar/model/domain/Car.java b/src/main/java/racingcar/model/domain/Car.java index cf29865d4..ca276fed9 100644 --- a/src/main/java/racingcar/model/domain/Car.java +++ b/src/main/java/racingcar/model/domain/Car.java @@ -4,7 +4,7 @@ import racingcar.model.constants.ErrorMessage; import racingcar.model.constants.GameRule; -public class Car { +public class Car implements Comparable { private final String name; private int position = 0; @@ -36,11 +36,12 @@ public String getName() { return name; } - public int getPosition() { - return position; - } - public CarDTO to() { return new CarDTO(name, position); } + + @Override + public int compareTo(Car o) { + return o.position - this.position; + } } diff --git a/src/main/java/racingcar/model/domain/RacingCars.java b/src/main/java/racingcar/model/domain/RacingCars.java index d77bfe408..ef6fdc945 100644 --- a/src/main/java/racingcar/model/domain/RacingCars.java +++ b/src/main/java/racingcar/model/domain/RacingCars.java @@ -4,7 +4,6 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; -import java.util.stream.Collectors; import racingcar.model.NumberGenerator; import racingcar.model.constants.ErrorMessage; import racingcar.model.constants.GameRule; @@ -34,9 +33,19 @@ public List cars() { return Collections.unmodifiableList(cars); } - public List findCarsAtPosition(int position) { - return cars.stream() - .filter(car -> car.getPosition() == position) - .collect(Collectors.toList()); + public List findWinnerNames() { + List winnerNames = new ArrayList<>(); + for (Car car : cars) { + if (findAnyHeadCar().compareTo(car) == 0) { + winnerNames.add(car.getName()); + } + } + return winnerNames; + } + + private Car findAnyHeadCar() { + List carsToSort = new ArrayList<>(cars); + Collections.sort(carsToSort); + return carsToSort.get(0); } } diff --git a/src/main/java/racingcar/view/util/FormatParser.java b/src/main/java/racingcar/view/util/FormatParser.java index 7a28aaf66..e11d8a81b 100644 --- a/src/main/java/racingcar/view/util/FormatParser.java +++ b/src/main/java/racingcar/view/util/FormatParser.java @@ -4,7 +4,6 @@ import java.util.Arrays; import java.util.List; -import java.util.StringJoiner; import java.util.stream.Collectors; import racingcar.view.constants.ErrorMessage; @@ -47,9 +46,7 @@ private static boolean hasEmptyValue(List values) { } public static String join(List values, String delimiter) { - StringJoiner joiner = new StringJoiner(delimiter); - values.forEach(joiner::add); - return joiner.toString(); + return String.join(delimiter, values); } public static String make(int count, String unit) { From 1c4f7755a21b00db33d888aab82ac70cb68a341e Mon Sep 17 00:00:00 2001 From: yoondgu Date: Thu, 15 Dec 2022 10:14:18 +0900 Subject: [PATCH 26/26] =?UTF-8?q?refactor:=20=EA=B0=9C=EB=B0=9C=20?= =?UTF-8?q?=EC=97=90=EB=9F=AC=EC=97=90=20=EB=8C=80=ED=95=9C=20=EC=98=88?= =?UTF-8?q?=EC=99=B8=EC=B2=98=EB=A6=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/racingcar/controller/RacingController.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/main/java/racingcar/controller/RacingController.java b/src/main/java/racingcar/controller/RacingController.java index 057512d5f..eb7c1ac78 100644 --- a/src/main/java/racingcar/controller/RacingController.java +++ b/src/main/java/racingcar/controller/RacingController.java @@ -14,9 +14,14 @@ public class RacingController { private final RacingGame racingGame = new RacingGame(new RandomNumberGenerator()); public void run() { - repeatUntilGetLegalAnswer(this::enrollCarToRace); - repeatUntilGetLegalAnswer(this::moveCarsByCount); - showWinners(); + try { + repeatUntilGetLegalAnswer(this::enrollCarToRace); + repeatUntilGetLegalAnswer(this::moveCarsByCount); + showWinners(); + } catch (Exception exception) { + exception.printStackTrace(); + outputView.printErrorMessage(exception.getMessage()); + } } private void repeatUntilGetLegalAnswer(Runnable runnable) {