From 0749d3b53b49858f2f79667bc7b63354f9f14318 Mon Sep 17 00:00:00 2001 From: cussle Date: Mon, 10 Jun 2024 03:59:07 +0900 Subject: [PATCH 01/16] =?UTF-8?q?docs:=20README.md=20=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=EC=B4=88=EA=B8=B0=20=EC=9E=91=EC=84=B1=20=ED=94=84=EB=A1=9C?= =?UTF-8?q?=EC=A0=9D=ED=8A=B8=EC=9D=98=20=EA=B0=9C=EC=9A=94,=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EB=AA=A9=EB=A1=9D,=20=EC=9A=94=EA=B5=AC=20?= =?UTF-8?q?=EC=82=AC=ED=95=AD=20=EB=93=B1=EC=9D=84=20=ED=8F=AC=ED=95=A8?= =?UTF-8?q?=ED=95=9C=20README.md=20=ED=8C=8C=EC=9D=BC=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 491aece1..211fc6c0 100644 --- a/README.md +++ b/README.md @@ -1 +1,37 @@ -# java-racingcar-precourse \ No newline at end of file +# 카테캠 미니과제 2 - 자동차 경주 + +## 개요 +- 카카오 테크 캠퍼스 2차 미니과제 +- 간단한 자동차 경주 게임을 구현 +- 사용자가 입력한 자동차 이름과 경주 횟수에 따라 자동차들이 랜덤하게 전진하며, 가장 멀리 이동한 자동차가 우승 + +## 기능 목록 + +1. **자동차 경주 게임 초기 설정** + - [ ] 자동차 이름 입력 및 검증 + - [ ] 경주 횟수 입력 및 검증 + +2. **자동차 객체 생성** + - [ ] 각 이름을 바탕으로 자동차 객체 생성 + - [ ] 자동차 객체는 이름과 위치를 가짐 + +3. **경주 로직 구현** + - [ ] 무작위 값을 통해 자동차의 전진 여부 결정 + - [ ] 각 회차 결과 출력 + +4. **우승자 결정 및 출력** + - [ ] 가장 멀리 이동한 자동차 결정 + - [ ] 여러 우승자가 있을 경우 쉼표로 구분하여 출력 + +5. **입력 오류 처리** + - [ ] 잘못된 입력에 대한 예외 처리 및 재입력 요청 + +6. **단위 테스트 작성** + - [ ] 주요 로직에 대한 단위 테스트 구현 + +## 요구 사항 +- JDK 17 +- Google Java Style Guide 준수 +- indent depth 2 이내 +- 함수 길이 15라인 이하 +- else, switch/case 사용 금지 From 991eba555f558deee530314f82ec46255762c688 Mon Sep 17 00:00:00 2001 From: cussle Date: Mon, 10 Jun 2024 04:16:48 +0900 Subject: [PATCH 02/16] =?UTF-8?q?feat:=20=EC=9E=90=EB=8F=99=EC=B0=A8=20?= =?UTF-8?q?=EC=9D=B4=EB=A6=84=20=EC=9E=85=EB=A0=A5=20=EB=B0=8F=20=EA=B2=80?= =?UTF-8?q?=EC=A6=9D=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 - 사용자로부터 쉼표(,)로 구분된 자동차 이름을 입력받고, 각 이름의 유효성을 검증하는 기능 구현 - 자동차 이름은 1자 이상 5자 이하로 제한 - 유효하지 않은 이름이 입력된 경우 IllegalArgumentException을 발생시키고, [ERROR] 메시지를 출력 - 유효한 이름을 리스트로 반환 --- README.md | 2 +- src/main/java/Application.java | 8 +++++ .../java/controller/CarRaceController.java | 18 ++++++++++ src/main/java/model/Car.java | 26 +++++++++++++++ src/main/java/view/InputView.java | 33 +++++++++++++++++++ src/main/java/view/OutputView.java | 7 ++++ src/test/java/model/CarTest.java | 28 ++++++++++++++++ 7 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 src/main/java/Application.java create mode 100644 src/main/java/controller/CarRaceController.java create mode 100644 src/main/java/model/Car.java create mode 100644 src/main/java/view/InputView.java create mode 100644 src/main/java/view/OutputView.java create mode 100644 src/test/java/model/CarTest.java diff --git a/README.md b/README.md index 211fc6c0..e1f8f482 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ ## 기능 목록 1. **자동차 경주 게임 초기 설정** - - [ ] 자동차 이름 입력 및 검증 + - [X] 자동차 이름 입력 및 검증 - [ ] 경주 횟수 입력 및 검증 2. **자동차 객체 생성** diff --git a/src/main/java/Application.java b/src/main/java/Application.java new file mode 100644 index 00000000..0da0a8db --- /dev/null +++ b/src/main/java/Application.java @@ -0,0 +1,8 @@ +import controller.CarRaceController; + +public class Application { + public static void main(String[] args) { + CarRaceController controller = new CarRaceController(); + controller.startRace(); + } +} diff --git a/src/main/java/controller/CarRaceController.java b/src/main/java/controller/CarRaceController.java new file mode 100644 index 00000000..d9281197 --- /dev/null +++ b/src/main/java/controller/CarRaceController.java @@ -0,0 +1,18 @@ +package controller; + +import java.util.List; +import view.InputView; +import view.OutputView; // OutputView 추후 필요 시 사용 + +public class CarRaceController { + public void startRace() { + try { + List carNames = InputView.getCarNames(); + // 추후 기능 확장 예정 + // TODO: 경주 시작 로직 추가 + } catch (IllegalArgumentException e) { + System.out.println(e.getMessage()); + } + } +} + diff --git a/src/main/java/model/Car.java b/src/main/java/model/Car.java new file mode 100644 index 00000000..155664d3 --- /dev/null +++ b/src/main/java/model/Car.java @@ -0,0 +1,26 @@ +package model; + +public class Car { + private String name; + private int position; + + public Car(String name) { + if (name.length() > 5 || name.isEmpty()) { + throw new IllegalArgumentException("[ERROR] 자동차 이름은 1자 이상 5자 이하만 가능합니다."); + } + this.name = name; + this.position = 0; + } + + public String getName() { + return name; + } + + public int getPosition() { + return position; + } + + public void move() { + this.position++; + } +} diff --git a/src/main/java/view/InputView.java b/src/main/java/view/InputView.java new file mode 100644 index 00000000..e8278a76 --- /dev/null +++ b/src/main/java/view/InputView.java @@ -0,0 +1,33 @@ +package view; + +import java.util.Arrays; +import java.util.List; +import java.util.Scanner; +import java.util.stream.Collectors; + +public class InputView { + private static final Scanner scanner = new Scanner(System.in); + + public static List getCarNames() { + System.out.println("경주할 자동차 이름을 입력하세요. (이름은 쉼표(,) 기준으로 구분)"); + String input = scanner.nextLine(); + List carNames = parseCarNames(input); + validateCarNames(carNames); + return carNames; + } + + private static List parseCarNames(String input) { + return Arrays.stream(input.split(",")) + .map(String::trim) + .collect(Collectors.toList()); + } + + private static void validateCarNames(List carNames) { + for (String name : carNames) { + if (name.length() > 5 || name.isEmpty()) { + throw new IllegalArgumentException("[ERROR] 자동차 이름은 1자 이상 5자 이하만 가능합니다."); + } + } + } +} + diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java new file mode 100644 index 00000000..f10f2fc7 --- /dev/null +++ b/src/main/java/view/OutputView.java @@ -0,0 +1,7 @@ +package view; + +public class OutputView { + public static void printMessage(String message) { + System.out.println(message); + } +} diff --git a/src/test/java/model/CarTest.java b/src/test/java/model/CarTest.java new file mode 100644 index 00000000..532f574e --- /dev/null +++ b/src/test/java/model/CarTest.java @@ -0,0 +1,28 @@ +package model; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +public class CarTest { + @Test + public void carNameShouldBeFiveCharsOrLess() { + IllegalArgumentException thrown = assertThrows( + IllegalArgumentException.class, + () -> new Car("abcdef") + ); + assertEquals("[ERROR] 자동차 이름은 1자 이상 5자 이하만 가능합니다.", thrown.getMessage()); + } + + @Test + public void carShouldStartAtPositionZero() { + Car car = new Car("test"); + assertEquals(0, car.getPosition()); + } + + @Test + public void carShouldMove() { + Car car = new Car("test"); + car.move(); + assertEquals(1, car.getPosition()); + } +} From 79d21b815e431e48ce528093696d03042cde268c Mon Sep 17 00:00:00 2001 From: cussle Date: Mon, 10 Jun 2024 04:27:05 +0900 Subject: [PATCH 03/16] =?UTF-8?q?refactor:=20=EC=9E=85=EB=A0=A5=EA=B3=BC?= =?UTF-8?q?=20=EC=B6=9C=EB=A0=A5=EC=9D=84=20BufferedReader=EC=99=80=20Stri?= =?UTF-8?q?ngBuilder=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Scanner 대신 BufferedReader를 사용하여 입력을 받도록 변경하여 입력 속도 개선 - 메시지 출력을 위해 StringBuilder를 사용하여 출력 성능 최적화 - IOException을 처리하여 입력 중 오류를 명확하게 처리 --- src/main/java/view/InputView.java | 21 +++++++++++++-------- src/main/java/view/OutputView.java | 3 ++- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/main/java/view/InputView.java b/src/main/java/view/InputView.java index e8278a76..c1b42c01 100644 --- a/src/main/java/view/InputView.java +++ b/src/main/java/view/InputView.java @@ -1,19 +1,25 @@ package view; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; import java.util.Arrays; import java.util.List; -import java.util.Scanner; import java.util.stream.Collectors; public class InputView { - private static final Scanner scanner = new Scanner(System.in); + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); public static List getCarNames() { - System.out.println("경주할 자동차 이름을 입력하세요. (이름은 쉼표(,) 기준으로 구분)"); - String input = scanner.nextLine(); - List carNames = parseCarNames(input); - validateCarNames(carNames); - return carNames; + System.out.println("경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)"); + try { + String input = br.readLine(); + List carNames = parseCarNames(input); + validateCarNames(carNames); + return carNames; + } catch (IOException e) { + throw new IllegalStateException("[ERROR] 입력을 읽는 중 오류가 발생했습니다.", e); + } } private static List parseCarNames(String input) { @@ -30,4 +36,3 @@ private static void validateCarNames(List carNames) { } } } - diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java index f10f2fc7..242d6f1f 100644 --- a/src/main/java/view/OutputView.java +++ b/src/main/java/view/OutputView.java @@ -2,6 +2,7 @@ public class OutputView { public static void printMessage(String message) { - System.out.println(message); + StringBuilder sb = new StringBuilder(message); + System.out.println(sb.toString()); } } From 80b080c5d24771555b2c1934de530315ba91d469 Mon Sep 17 00:00:00 2001 From: cussle Date: Mon, 10 Jun 2024 04:30:29 +0900 Subject: [PATCH 04/16] =?UTF-8?q?feat:=20=EA=B2=BD=EC=A3=BC=20=ED=9A=9F?= =?UTF-8?q?=EC=88=98=20=EC=9E=85=EB=A0=A5=20=EB=B0=8F=20=EA=B2=80=EC=A6=9D?= =?UTF-8?q?=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 - 사용자로부터 경주 횟수를 입력받고 이를 검증하는 기능 추가 - 경주 횟수는 유효한 숫자여야 하며, 1 이상이어야 함 - 유효하지 않은 입력에 대해 IllegalArgumentException을 발생시키고, [ERROR] 메시지를 출력 --- README.md | 2 +- .../java/controller/CarRaceController.java | 6 ++--- src/main/java/view/InputView.java | 22 +++++++++++++++++++ 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index e1f8f482..1e8e436f 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ 1. **자동차 경주 게임 초기 설정** - [X] 자동차 이름 입력 및 검증 - - [ ] 경주 횟수 입력 및 검증 + - [X] 경주 횟수 입력 및 검증 2. **자동차 객체 생성** - [ ] 각 이름을 바탕으로 자동차 객체 생성 diff --git a/src/main/java/controller/CarRaceController.java b/src/main/java/controller/CarRaceController.java index d9281197..305f0366 100644 --- a/src/main/java/controller/CarRaceController.java +++ b/src/main/java/controller/CarRaceController.java @@ -2,17 +2,17 @@ import java.util.List; import view.InputView; -import view.OutputView; // OutputView 추후 필요 시 사용 +import view.OutputView; public class CarRaceController { public void startRace() { try { List carNames = InputView.getCarNames(); + int raceCount = InputView.getRaceCount(); // 추후 기능 확장 예정 // TODO: 경주 시작 로직 추가 } catch (IllegalArgumentException e) { - System.out.println(e.getMessage()); + OutputView.printMessage(e.getMessage()); } } } - diff --git a/src/main/java/view/InputView.java b/src/main/java/view/InputView.java index c1b42c01..04b69156 100644 --- a/src/main/java/view/InputView.java +++ b/src/main/java/view/InputView.java @@ -22,6 +22,16 @@ public static List getCarNames() { } } + public static int getRaceCount() { + System.out.println("시도할 회수는 몇회인가요?"); + try { + String input = br.readLine(); + return validateRaceCount(input); + } catch (IOException e) { + throw new IllegalStateException("[ERROR] 입력을 읽는 중 오류가 발생했습니다.", e); + } + } + private static List parseCarNames(String input) { return Arrays.stream(input.split(",")) .map(String::trim) @@ -35,4 +45,16 @@ private static void validateCarNames(List carNames) { } } } + + private static int validateRaceCount(String input) { + try { + int raceCount = Integer.parseInt(input); + if (raceCount <= 0) { + throw new IllegalArgumentException("[ERROR] 경주 횟수는 1 이상이어야 합니다."); + } + return raceCount; + } catch (NumberFormatException e) { + throw new IllegalArgumentException("[ERROR] 유효한 숫자를 입력하세요."); + } + } } From b51563497934a761d411182b124fcdd58978582b Mon Sep 17 00:00:00 2001 From: cussle Date: Mon, 10 Jun 2024 04:35:17 +0900 Subject: [PATCH 05/16] =?UTF-8?q?feat:=20=EC=9E=85=EB=A0=A5=EB=90=9C=20?= =?UTF-8?q?=EC=9E=90=EB=8F=99=EC=B0=A8=20=EC=9D=B4=EB=A6=84=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20Car=20=EA=B0=9D=EC=B2=B4=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 각 이름을 기반으로 Car 객체를 생성 - Car 객체는 이름을 가지며, 위치는 아직 초기화하지 않음 --- README.md | 2 +- src/main/java/controller/CarRaceController.java | 14 ++++++++++++-- src/main/java/model/Car.java | 6 +++++- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 1e8e436f..afd5b633 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ - [X] 경주 횟수 입력 및 검증 2. **자동차 객체 생성** - - [ ] 각 이름을 바탕으로 자동차 객체 생성 + - [X] 각 이름을 바탕으로 자동차 객체 생성 - [ ] 자동차 객체는 이름과 위치를 가짐 3. **경주 로직 구현** diff --git a/src/main/java/controller/CarRaceController.java b/src/main/java/controller/CarRaceController.java index 305f0366..c93c782b 100644 --- a/src/main/java/controller/CarRaceController.java +++ b/src/main/java/controller/CarRaceController.java @@ -1,18 +1,28 @@ package controller; -import java.util.List; +import model.Car; import view.InputView; import view.OutputView; +import java.util.List; +import java.util.stream.Collectors; + public class CarRaceController { public void startRace() { try { List carNames = InputView.getCarNames(); int raceCount = InputView.getRaceCount(); - // 추후 기능 확장 예정 + + List cars = createCars(carNames); // TODO: 경주 시작 로직 추가 } catch (IllegalArgumentException e) { OutputView.printMessage(e.getMessage()); } } + + private List createCars(List carNames) { + return carNames.stream() + .map(Car::new) + .collect(Collectors.toList()); + } } diff --git a/src/main/java/model/Car.java b/src/main/java/model/Car.java index 155664d3..31184dfd 100644 --- a/src/main/java/model/Car.java +++ b/src/main/java/model/Car.java @@ -9,7 +9,7 @@ public Car(String name) { throw new IllegalArgumentException("[ERROR] 자동차 이름은 1자 이상 5자 이하만 가능합니다."); } this.name = name; - this.position = 0; + // 위치 초기화는 나중에 } public String getName() { @@ -20,6 +20,10 @@ public int getPosition() { return position; } + public void initializePosition() { + this.position = 0; + } + public void move() { this.position++; } From 724305236e603fe34d07bc882684f06bafa0d2e0 Mon Sep 17 00:00:00 2001 From: cussle Date: Mon, 10 Jun 2024 04:38:53 +0900 Subject: [PATCH 06/16] =?UTF-8?q?feat:=20=EC=9E=90=EB=8F=99=EC=B0=A8=20?= =?UTF-8?q?=EA=B0=9D=EC=B2=B4=EC=97=90=20=EC=9C=84=EC=B9=98=20=EC=B4=88?= =?UTF-8?q?=EA=B8=B0=ED=99=94=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Car 객체 생성 후 위치를 초기화하는 로직 추가 - 각 Car 객체는 이름과 초기 위치(0)를 가짐 --- README.md | 2 +- src/main/java/controller/CarRaceController.java | 10 +++++++--- src/main/java/model/Car.java | 1 - 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index afd5b633..1bab846d 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ 2. **자동차 객체 생성** - [X] 각 이름을 바탕으로 자동차 객체 생성 - - [ ] 자동차 객체는 이름과 위치를 가짐 + - [X] 자동차 객체는 이름과 위치를 가짐 3. **경주 로직 구현** - [ ] 무작위 값을 통해 자동차의 전진 여부 결정 diff --git a/src/main/java/controller/CarRaceController.java b/src/main/java/controller/CarRaceController.java index c93c782b..19f3191e 100644 --- a/src/main/java/controller/CarRaceController.java +++ b/src/main/java/controller/CarRaceController.java @@ -13,16 +13,20 @@ public void startRace() { List carNames = InputView.getCarNames(); int raceCount = InputView.getRaceCount(); - List cars = createCars(carNames); + List cars = createAndInitializeCars(carNames); // TODO: 경주 시작 로직 추가 } catch (IllegalArgumentException e) { OutputView.printMessage(e.getMessage()); } } - private List createCars(List carNames) { + private List createAndInitializeCars(List carNames) { return carNames.stream() - .map(Car::new) + .map(name -> { + Car car = new Car(name); + car.initializePosition(); // 위치 초기화 + return car; + }) .collect(Collectors.toList()); } } diff --git a/src/main/java/model/Car.java b/src/main/java/model/Car.java index 31184dfd..357e9611 100644 --- a/src/main/java/model/Car.java +++ b/src/main/java/model/Car.java @@ -9,7 +9,6 @@ public Car(String name) { throw new IllegalArgumentException("[ERROR] 자동차 이름은 1자 이상 5자 이하만 가능합니다."); } this.name = name; - // 위치 초기화는 나중에 } public String getName() { From 30bb7e217dae9b9804997f649c724e8a564d72d0 Mon Sep 17 00:00:00 2001 From: cussle Date: Mon, 10 Jun 2024 04:41:04 +0900 Subject: [PATCH 07/16] =?UTF-8?q?feat:=20=EB=AC=B4=EC=9E=91=EC=9C=84=20?= =?UTF-8?q?=EA=B0=92=EC=9D=84=20=ED=86=B5=ED=95=B4=20=EC=9E=90=EB=8F=99?= =?UTF-8?q?=EC=B0=A8=EC=9D=98=20=EC=A0=84=EC=A7=84=20=EC=97=AC=EB=B6=80=20?= =?UTF-8?q?=EA=B2=B0=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 각 자동차가 무작위 값을 통해 전진 여부를 결정하는 로직 추가 - 무작위 값이 4 이상이면 자동차가 한 칸 전진 --- README.md | 2 +- src/main/java/controller/CarRaceController.java | 14 +++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1bab846d..3b5e287f 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ - [X] 자동차 객체는 이름과 위치를 가짐 3. **경주 로직 구현** - - [ ] 무작위 값을 통해 자동차의 전진 여부 결정 + - [X] 무작위 값을 통해 자동차의 전진 여부 결정 - [ ] 각 회차 결과 출력 4. **우승자 결정 및 출력** diff --git a/src/main/java/controller/CarRaceController.java b/src/main/java/controller/CarRaceController.java index 19f3191e..d498ecf2 100644 --- a/src/main/java/controller/CarRaceController.java +++ b/src/main/java/controller/CarRaceController.java @@ -5,6 +5,7 @@ import view.OutputView; import java.util.List; +import java.util.Random; import java.util.stream.Collectors; public class CarRaceController { @@ -14,7 +15,7 @@ public void startRace() { int raceCount = InputView.getRaceCount(); List cars = createAndInitializeCars(carNames); - // TODO: 경주 시작 로직 추가 + runRace(cars, raceCount); } catch (IllegalArgumentException e) { OutputView.printMessage(e.getMessage()); } @@ -29,4 +30,15 @@ private List createAndInitializeCars(List carNames) { }) .collect(Collectors.toList()); } + + private void runRace(List cars, int raceCount) { + Random random = new Random(); + for (int i = 0; i < raceCount; i++) { + for (Car car : cars) { + if (random.nextInt(10) >= 4) { // 4 이상이면 전진 + car.move(); + } + } + } + } } From 42f4bfa2c17476445729335fddde04c059954548 Mon Sep 17 00:00:00 2001 From: cussle Date: Mon, 10 Jun 2024 04:43:00 +0900 Subject: [PATCH 08/16] =?UTF-8?q?feat:=20=EA=B0=81=20=ED=9A=8C=EC=B0=A8=20?= =?UTF-8?q?=EA=B2=B0=EA=B3=BC=20=EC=B6=9C=EB=A0=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 경주가 진행되는 동안 각 회차의 결과를 출력하는 로직 추가 - 자동차의 이름과 현재 위치를 '-' 문자로 표시 --- README.md | 2 +- src/main/java/controller/CarRaceController.java | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3b5e287f..8ec36f3c 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ 3. **경주 로직 구현** - [X] 무작위 값을 통해 자동차의 전진 여부 결정 - - [ ] 각 회차 결과 출력 + - [X] 각 회차 결과 출력 4. **우승자 결정 및 출력** - [ ] 가장 멀리 이동한 자동차 결정 diff --git a/src/main/java/controller/CarRaceController.java b/src/main/java/controller/CarRaceController.java index d498ecf2..5837feb0 100644 --- a/src/main/java/controller/CarRaceController.java +++ b/src/main/java/controller/CarRaceController.java @@ -39,6 +39,19 @@ private void runRace(List cars, int raceCount) { car.move(); } } + printRaceStatus(cars); // 각 회차 결과 출력 } } + + private void printRaceStatus(List cars) { + for (Car car : cars) { + StringBuilder status = new StringBuilder(); + status.append(car.getName()).append(" : "); + for (int i = 0; i < car.getPosition(); i++) { + status.append("-"); + } + OutputView.printMessage(status.toString()); + } + OutputView.printMessage(""); // 빈 줄로 회차 구분 + } } From d2b70c11c0643d11ca8a38492f328c128cefe264 Mon Sep 17 00:00:00 2001 From: cussle Date: Mon, 10 Jun 2024 04:47:15 +0900 Subject: [PATCH 09/16] =?UTF-8?q?feat:=20=EA=B0=80=EC=9E=A5=20=EB=A9=80?= =?UTF-8?q?=EB=A6=AC=20=EC=9D=B4=EB=8F=99=ED=95=9C=20=EC=9E=90=EB=8F=99?= =?UTF-8?q?=EC=B0=A8=20=EA=B2=B0=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 경주가 끝난 후 모든 자동차 중에서 가장 멀리 이동한 자동차를 결정하는 로직 추가 - 최대 이동 거리를 계산하고, 이를 바탕으로 우승자를 선정 --- README.md | 2 +- src/main/java/controller/CarRaceController.java | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8ec36f3c..4b386934 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ - [X] 각 회차 결과 출력 4. **우승자 결정 및 출력** - - [ ] 가장 멀리 이동한 자동차 결정 + - [X] 가장 멀리 이동한 자동차 결정 - [ ] 여러 우승자가 있을 경우 쉼표로 구분하여 출력 5. **입력 오류 처리** diff --git a/src/main/java/controller/CarRaceController.java b/src/main/java/controller/CarRaceController.java index 5837feb0..f2e5cd0d 100644 --- a/src/main/java/controller/CarRaceController.java +++ b/src/main/java/controller/CarRaceController.java @@ -16,6 +16,8 @@ public void startRace() { List cars = createAndInitializeCars(carNames); runRace(cars, raceCount); + List winners = determineWinners(cars); // 우승자 결정 + // TODO: 우승자 출력 로직 추가 } catch (IllegalArgumentException e) { OutputView.printMessage(e.getMessage()); } @@ -54,4 +56,15 @@ private void printRaceStatus(List cars) { } OutputView.printMessage(""); // 빈 줄로 회차 구분 } + + private List determineWinners(List cars) { + int maxPosition = cars.stream() + .mapToInt(Car::getPosition) + .max() + .orElse(0); // 최대 거리 계산 + + return cars.stream() + .filter(car -> car.getPosition() == maxPosition) + .collect(Collectors.toList()); + } } From 0a1dfa111a199ed83e6a431a81bcad6ed7cc30a7 Mon Sep 17 00:00:00 2001 From: cussle Date: Mon, 10 Jun 2024 04:48:35 +0900 Subject: [PATCH 10/16] =?UTF-8?q?feat:=20=EC=97=AC=EB=9F=AC=20=EC=9A=B0?= =?UTF-8?q?=EC=8A=B9=EC=9E=90=EA=B0=80=20=EC=9E=88=EC=9D=84=20=EA=B2=BD?= =?UTF-8?q?=EC=9A=B0=20=EC=89=BC=ED=91=9C=EB=A1=9C=20=EA=B5=AC=EB=B6=84?= =?UTF-8?q?=ED=95=98=EC=97=AC=20=EC=B6=9C=EB=A0=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 경주가 끝난 후 우승자를 출력하는 로직 추가 - 우승자가 여러 명일 경우 쉼표로 구분하여 출력 - 우승자가 한 명일 때와 여러 명일 때 모두 지원 --- README.md | 2 +- src/main/java/controller/CarRaceController.java | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4b386934..f95f139e 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ 4. **우승자 결정 및 출력** - [X] 가장 멀리 이동한 자동차 결정 - - [ ] 여러 우승자가 있을 경우 쉼표로 구분하여 출력 + - [X] 여러 우승자가 있을 경우 쉼표로 구분하여 출력 5. **입력 오류 처리** - [ ] 잘못된 입력에 대한 예외 처리 및 재입력 요청 diff --git a/src/main/java/controller/CarRaceController.java b/src/main/java/controller/CarRaceController.java index f2e5cd0d..50f2085f 100644 --- a/src/main/java/controller/CarRaceController.java +++ b/src/main/java/controller/CarRaceController.java @@ -17,7 +17,7 @@ public void startRace() { List cars = createAndInitializeCars(carNames); runRace(cars, raceCount); List winners = determineWinners(cars); // 우승자 결정 - // TODO: 우승자 출력 로직 추가 + printWinners(winners); // 우승자 출력 } catch (IllegalArgumentException e) { OutputView.printMessage(e.getMessage()); } @@ -67,4 +67,11 @@ private List determineWinners(List cars) { .filter(car -> car.getPosition() == maxPosition) .collect(Collectors.toList()); } + + private void printWinners(List winners) { + String winnerNames = winners.stream() + .map(Car::getName) + .collect(Collectors.joining(", ")); + OutputView.printMessage("최종 우승자 : " + winnerNames); + } } From 8bca5c0055c7858b228af2bb807636d3b3ba2402 Mon Sep 17 00:00:00 2001 From: cussle Date: Mon, 10 Jun 2024 04:55:35 +0900 Subject: [PATCH 11/16] =?UTF-8?q?feat:=20=EC=9E=98=EB=AA=BB=EB=90=9C=20?= =?UTF-8?q?=EC=9E=85=EB=A0=A5=EC=97=90=20=EB=8C=80=ED=95=9C=20=EC=98=88?= =?UTF-8?q?=EC=99=B8=20=EC=B2=98=EB=A6=AC=20=EB=B0=8F=20=EC=9E=AC=EC=9E=85?= =?UTF-8?q?=EB=A0=A5=20=EC=9A=94=EC=B2=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 잘못된 자동차 이름이나 경주 횟수 입력 시 예외를 처리하고 재입력을 요청하는 로직 추가 - 유효하지 않은 입력에 대해 [ERROR] 메시지를 출력하고, 올바른 값이 입력될 때까지 재입력 요청 --- README.md | 2 +- .../java/controller/CarRaceController.java | 16 ++++----- src/main/java/view/InputView.java | 36 +++++++++++-------- 3 files changed, 29 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index f95f139e..d793d299 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ - [X] 여러 우승자가 있을 경우 쉼표로 구분하여 출력 5. **입력 오류 처리** - - [ ] 잘못된 입력에 대한 예외 처리 및 재입력 요청 + - [X] 잘못된 입력에 대한 예외 처리 및 재입력 요청 6. **단위 테스트 작성** - [ ] 주요 로직에 대한 단위 테스트 구현 diff --git a/src/main/java/controller/CarRaceController.java b/src/main/java/controller/CarRaceController.java index 50f2085f..3e6cd3a0 100644 --- a/src/main/java/controller/CarRaceController.java +++ b/src/main/java/controller/CarRaceController.java @@ -10,17 +10,13 @@ public class CarRaceController { public void startRace() { - try { - List carNames = InputView.getCarNames(); - int raceCount = InputView.getRaceCount(); + List carNames = InputView.getCarNames(); + int raceCount = InputView.getRaceCount(); - List cars = createAndInitializeCars(carNames); - runRace(cars, raceCount); - List winners = determineWinners(cars); // 우승자 결정 - printWinners(winners); // 우승자 출력 - } catch (IllegalArgumentException e) { - OutputView.printMessage(e.getMessage()); - } + List cars = createAndInitializeCars(carNames); + runRace(cars, raceCount); + List winners = determineWinners(cars); // 우승자 결정 + printWinners(winners); // 우승자 출력 } private List createAndInitializeCars(List carNames) { diff --git a/src/main/java/view/InputView.java b/src/main/java/view/InputView.java index 04b69156..9301ce27 100644 --- a/src/main/java/view/InputView.java +++ b/src/main/java/view/InputView.java @@ -11,24 +11,32 @@ public class InputView { private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); public static List getCarNames() { - System.out.println("경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)"); - try { - String input = br.readLine(); - List carNames = parseCarNames(input); - validateCarNames(carNames); - return carNames; - } catch (IOException e) { - throw new IllegalStateException("[ERROR] 입력을 읽는 중 오류가 발생했습니다.", e); + while (true) { + try { + System.out.println("경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)"); + String input = br.readLine(); + List carNames = parseCarNames(input); + validateCarNames(carNames); + return carNames; + } catch (IOException e) { + System.out.println("[ERROR] 입력을 읽는 중 오류가 발생했습니다. 다시 시도해주세요."); + } catch (IllegalArgumentException e) { + System.out.println(e.getMessage()); + } } } public static int getRaceCount() { - System.out.println("시도할 회수는 몇회인가요?"); - try { - String input = br.readLine(); - return validateRaceCount(input); - } catch (IOException e) { - throw new IllegalStateException("[ERROR] 입력을 읽는 중 오류가 발생했습니다.", e); + while (true) { + try { + System.out.println("시도할 횟수는 몇회인가요?"); + String input = br.readLine(); + return validateRaceCount(input); + } catch (IOException e) { + System.out.println("[ERROR] 입력을 읽는 중 오류가 발생했습니다. 다시 시도해주세요."); + } catch (IllegalArgumentException e) { + System.out.println(e.getMessage()); + } } } From 6a8f92caa15f7accaa0f5022046bd8a066c2261c Mon Sep 17 00:00:00 2001 From: cussle Date: Mon, 10 Jun 2024 05:00:12 +0900 Subject: [PATCH 12/16] =?UTF-8?q?test:=20=EC=A3=BC=EC=9A=94=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=EC=97=90=20=EB=8C=80=ED=95=9C=20=EB=8B=A8=EC=9C=84=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Car 클래스의 이름 유효성, 위치 초기화 및 전진 기능 테스트 추가 --- README.md | 2 +- src/test/java/model/CarTest.java | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d793d299..8c434f96 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ - [X] 잘못된 입력에 대한 예외 처리 및 재입력 요청 6. **단위 테스트 작성** - - [ ] 주요 로직에 대한 단위 테스트 구현 + - [X] 주요 로직에 대한 단위 테스트 구현 ## 요구 사항 - JDK 17 diff --git a/src/test/java/model/CarTest.java b/src/test/java/model/CarTest.java index 532f574e..d4bb25cc 100644 --- a/src/test/java/model/CarTest.java +++ b/src/test/java/model/CarTest.java @@ -13,15 +13,26 @@ public void carNameShouldBeFiveCharsOrLess() { assertEquals("[ERROR] 자동차 이름은 1자 이상 5자 이하만 가능합니다.", thrown.getMessage()); } + @Test + public void carNameShouldNotBeEmpty() { + IllegalArgumentException thrown = assertThrows( + IllegalArgumentException.class, + () -> new Car("") + ); + assertEquals("[ERROR] 자동차 이름은 1자 이상 5자 이하만 가능합니다.", thrown.getMessage()); + } + @Test public void carShouldStartAtPositionZero() { Car car = new Car("test"); + car.initializePosition(); assertEquals(0, car.getPosition()); } @Test public void carShouldMove() { Car car = new Car("test"); + car.initializePosition(); car.move(); assertEquals(1, car.getPosition()); } From 48a930935d5ad92f00017a8da9823feb75669a30 Mon Sep 17 00:00:00 2001 From: cussle Date: Mon, 10 Jun 2024 05:13:48 +0900 Subject: [PATCH 13/16] =?UTF-8?q?refactor:=20=EB=AA=A8=EB=8D=B8=20?= =?UTF-8?q?=ED=99=95=EC=9E=A5=20=EB=B0=8F=20=EC=BB=A8=ED=8A=B8=EB=A1=A4?= =?UTF-8?q?=EB=9F=AC=20=EB=8B=A8=EC=88=9C=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 레이스 로직을 처리하기 위한 Race 클래스를 추가하여 모델 확장 - CarRaceController의 책임을 단순화하여 사용자 입력 및 레이스 관리 역할만 수행 - Race 클래스에 대한 단위 테스트 추가 - System.out.println을 OutputView.printMessage로 대체하여 출력 관리 개선 --- .../java/controller/CarRaceController.java | 65 ++++-------------- src/main/java/model/Race.java | 67 +++++++++++++++++++ src/main/java/view/InputView.java | 16 +++-- src/test/java/model/RaceTest.java | 40 +++++++++++ 4 files changed, 129 insertions(+), 59 deletions(-) create mode 100644 src/main/java/model/Race.java create mode 100644 src/test/java/model/RaceTest.java diff --git a/src/main/java/controller/CarRaceController.java b/src/main/java/controller/CarRaceController.java index 3e6cd3a0..32f64bfe 100644 --- a/src/main/java/controller/CarRaceController.java +++ b/src/main/java/controller/CarRaceController.java @@ -1,67 +1,26 @@ package controller; +import java.util.List; +import java.util.stream.Collectors; import model.Car; +import model.Race; import view.InputView; import view.OutputView; -import java.util.List; -import java.util.Random; -import java.util.stream.Collectors; - public class CarRaceController { public void startRace() { - List carNames = InputView.getCarNames(); - int raceCount = InputView.getRaceCount(); - - List cars = createAndInitializeCars(carNames); - runRace(cars, raceCount); - List winners = determineWinners(cars); // 우승자 결정 - printWinners(winners); // 우승자 출력 - } + try { + List carNames = InputView.getCarNames(); + int raceCount = InputView.getRaceCount(); - private List createAndInitializeCars(List carNames) { - return carNames.stream() - .map(name -> { - Car car = new Car(name); - car.initializePosition(); // 위치 초기화 - return car; - }) - .collect(Collectors.toList()); - } - - private void runRace(List cars, int raceCount) { - Random random = new Random(); - for (int i = 0; i < raceCount; i++) { - for (Car car : cars) { - if (random.nextInt(10) >= 4) { // 4 이상이면 전진 - car.move(); - } - } - printRaceStatus(cars); // 각 회차 결과 출력 - } - } + Race race = new Race(carNames); + race.run(raceCount); - private void printRaceStatus(List cars) { - for (Car car : cars) { - StringBuilder status = new StringBuilder(); - status.append(car.getName()).append(" : "); - for (int i = 0; i < car.getPosition(); i++) { - status.append("-"); - } - OutputView.printMessage(status.toString()); + List winners = race.getWinners(); + printWinners(winners); + } catch (IllegalArgumentException e) { + OutputView.printMessage(e.getMessage()); } - OutputView.printMessage(""); // 빈 줄로 회차 구분 - } - - private List determineWinners(List cars) { - int maxPosition = cars.stream() - .mapToInt(Car::getPosition) - .max() - .orElse(0); // 최대 거리 계산 - - return cars.stream() - .filter(car -> car.getPosition() == maxPosition) - .collect(Collectors.toList()); } private void printWinners(List winners) { diff --git a/src/main/java/model/Race.java b/src/main/java/model/Race.java new file mode 100644 index 00000000..3b46f4be --- /dev/null +++ b/src/main/java/model/Race.java @@ -0,0 +1,67 @@ +package model; + +import java.util.List; +import java.util.Random; +import java.util.stream.Collectors; + +public class Race { + private final List cars; + private static final int FORWARD_THRESHOLD = 4; + private static final int RANDOM_BOUND = 10; + + public Race(List carNames) { + this.cars = carNames.stream() + .map(Car::new) + .collect(Collectors.toList()); + initializeCars(); + } + + private void initializeCars() { + for (Car car : cars) { + car.initializePosition(); + } + } + + public List getCars() { + return cars; + } + + public void run(int raceCount) { + for (int i = 0; i < raceCount; i++) { + moveCars(); + printRaceStatus(); + } + } + + private void moveCars() { + Random random = new Random(); + for (Car car : cars) { + if (random.nextInt(RANDOM_BOUND) >= FORWARD_THRESHOLD) { + car.move(); + } + } + } + + private void printRaceStatus() { + for (Car car : cars) { + StringBuilder status = new StringBuilder(); + status.append(car.getName()).append(" : "); + for (int i = 0; i < car.getPosition(); i++) { + status.append("-"); + } + System.out.println(status.toString()); + } + System.out.println(""); // 빈 줄로 회차 구분 + } + + public List getWinners() { + int maxPosition = cars.stream() + .mapToInt(Car::getPosition) + .max() + .orElse(0); + + return cars.stream() + .filter(car -> car.getPosition() == maxPosition) + .collect(Collectors.toList()); + } +} diff --git a/src/main/java/view/InputView.java b/src/main/java/view/InputView.java index 9301ce27..ce7193f3 100644 --- a/src/main/java/view/InputView.java +++ b/src/main/java/view/InputView.java @@ -13,15 +13,15 @@ public class InputView { public static List getCarNames() { while (true) { try { - System.out.println("경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)"); + OutputView.printMessage("경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)"); String input = br.readLine(); List carNames = parseCarNames(input); validateCarNames(carNames); return carNames; } catch (IOException e) { - System.out.println("[ERROR] 입력을 읽는 중 오류가 발생했습니다. 다시 시도해주세요."); + handleInputError("[ERROR] 입력을 읽는 중 오류가 발생했습니다. 다시 시도해주세요."); } catch (IllegalArgumentException e) { - System.out.println(e.getMessage()); + handleInputError(e.getMessage()); } } } @@ -29,17 +29,21 @@ public static List getCarNames() { public static int getRaceCount() { while (true) { try { - System.out.println("시도할 횟수는 몇회인가요?"); + OutputView.printMessage("시도할 횟수는 몇회인가요?"); String input = br.readLine(); return validateRaceCount(input); } catch (IOException e) { - System.out.println("[ERROR] 입력을 읽는 중 오류가 발생했습니다. 다시 시도해주세요."); + handleInputError("[ERROR] 입력을 읽는 중 오류가 발생했습니다. 다시 시도해주세요."); } catch (IllegalArgumentException e) { - System.out.println(e.getMessage()); + handleInputError(e.getMessage()); } } } + private static void handleInputError(String message) { + OutputView.printMessage(message); + } + private static List parseCarNames(String input) { return Arrays.stream(input.split(",")) .map(String::trim) diff --git a/src/test/java/model/RaceTest.java b/src/test/java/model/RaceTest.java new file mode 100644 index 00000000..297d39c1 --- /dev/null +++ b/src/test/java/model/RaceTest.java @@ -0,0 +1,40 @@ +package model; + +import org.junit.jupiter.api.Test; +import java.util.List; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.tuple; + +public class RaceTest { + @Test + public void shouldCreateAndInitializeCars() { + List carNames = List.of("pobi", "woni", "jun"); + Race race = new Race(carNames); + + assertThat(race.getCars()).hasSize(3); + assertThat(race.getCars()).extracting("name", "position") + .containsExactly( + tuple("pobi", 0), + tuple("woni", 0), + tuple("jun", 0) + ); + } + + @Test + public void shouldDetermineWinners() { + List carNames = List.of("pobi", "woni", "jun"); + Race race = new Race(carNames); + + // 강제로 차를 움직여 우승자 결정 + race.getCars().get(0).move(); + race.getCars().get(0).move(); + race.getCars().get(1).move(); + race.getCars().get(2).move(); + race.getCars().get(2).move(); + + List winners = race.getWinners(); + assertThat(winners).hasSize(2); + assertThat(winners).extracting("name") + .contains("pobi", "jun"); + } +} From 4f5f2d1293f38faf141ee2cf74b1389cf1fc3cf4 Mon Sep 17 00:00:00 2001 From: cussle Date: Mon, 10 Jun 2024 05:22:20 +0900 Subject: [PATCH 14/16] =?UTF-8?q?refactor:=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EA=B0=9C=EC=84=A0=20=EB=B0=8F=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EA=B0=95=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - OutputView에서 불필요한 StringBuilder 사용 제거 - 더 많은 테스트 케이스를 추가하여 모델의 신뢰성 향상 - Race 클래스의 출력 로직을 OutputView로 이동하여 책임 분리 - Random 객체를 static final로 선언하여 성능 최적화 --- src/main/java/model/Race.java | 19 ++++--------------- src/main/java/view/OutputView.java | 18 ++++++++++++++++-- src/test/java/model/CarTest.java | 15 +++++++++++++++ src/test/java/model/RaceTest.java | 29 +++++++++++++++++++++++++++++ 4 files changed, 64 insertions(+), 17 deletions(-) diff --git a/src/main/java/model/Race.java b/src/main/java/model/Race.java index 3b46f4be..c1569a66 100644 --- a/src/main/java/model/Race.java +++ b/src/main/java/model/Race.java @@ -2,12 +2,14 @@ import java.util.List; import java.util.Random; +import view.OutputView; import java.util.stream.Collectors; public class Race { private final List cars; private static final int FORWARD_THRESHOLD = 4; private static final int RANDOM_BOUND = 10; + private static final Random RANDOM = new Random(); // static final로 변경 public Race(List carNames) { this.cars = carNames.stream() @@ -29,31 +31,18 @@ public List getCars() { public void run(int raceCount) { for (int i = 0; i < raceCount; i++) { moveCars(); - printRaceStatus(); + OutputView.printRaceStatus(cars); // 출력 로직을 외부로 이동 } } private void moveCars() { - Random random = new Random(); for (Car car : cars) { - if (random.nextInt(RANDOM_BOUND) >= FORWARD_THRESHOLD) { + if (RANDOM.nextInt(RANDOM_BOUND) >= FORWARD_THRESHOLD) { car.move(); } } } - private void printRaceStatus() { - for (Car car : cars) { - StringBuilder status = new StringBuilder(); - status.append(car.getName()).append(" : "); - for (int i = 0; i < car.getPosition(); i++) { - status.append("-"); - } - System.out.println(status.toString()); - } - System.out.println(""); // 빈 줄로 회차 구분 - } - public List getWinners() { int maxPosition = cars.stream() .mapToInt(Car::getPosition) diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java index 242d6f1f..81473812 100644 --- a/src/main/java/view/OutputView.java +++ b/src/main/java/view/OutputView.java @@ -1,8 +1,22 @@ package view; +import model.Car; +import java.util.List; + public class OutputView { public static void printMessage(String message) { - StringBuilder sb = new StringBuilder(message); - System.out.println(sb.toString()); + System.out.println(message); + } + + public static void printRaceStatus(List cars) { + for (Car car : cars) { + StringBuilder status = new StringBuilder(); + status.append(car.getName()).append(" : "); + for (int i = 0; i < car.getPosition(); i++) { + status.append("-"); + } + System.out.println(status.toString()); + } + System.out.println(); // 빈 줄로 회차 구분 } } diff --git a/src/test/java/model/CarTest.java b/src/test/java/model/CarTest.java index d4bb25cc..0fba60bc 100644 --- a/src/test/java/model/CarTest.java +++ b/src/test/java/model/CarTest.java @@ -22,6 +22,12 @@ public void carNameShouldNotBeEmpty() { assertEquals("[ERROR] 자동차 이름은 1자 이상 5자 이하만 가능합니다.", thrown.getMessage()); } + @Test + public void carNameShouldBeSetCorrectly() { + Car car = new Car("pobi"); + assertEquals("pobi", car.getName()); + } + @Test public void carShouldStartAtPositionZero() { Car car = new Car("test"); @@ -36,4 +42,13 @@ public void carShouldMove() { car.move(); assertEquals(1, car.getPosition()); } + + @Test + public void carShouldMoveMultipleTimes() { + Car car = new Car("test"); + car.initializePosition(); + car.move(); + car.move(); + assertEquals(2, car.getPosition()); + } } diff --git a/src/test/java/model/RaceTest.java b/src/test/java/model/RaceTest.java index 297d39c1..5c0e73c5 100644 --- a/src/test/java/model/RaceTest.java +++ b/src/test/java/model/RaceTest.java @@ -37,4 +37,33 @@ public void shouldDetermineWinners() { assertThat(winners).extracting("name") .contains("pobi", "jun"); } + + @Test + public void shouldRunRaceAndMoveCars() { + List carNames = List.of("pobi", "woni", "jun"); + Race race = new Race(carNames); + + race.run(5); + + assertThat(race.getCars()).allSatisfy(car -> + assertThat(car.getPosition()).isGreaterThanOrEqualTo(0) + ); + } + + @Test + public void shouldHaveMultipleWinnersIfTied() { + List carNames = List.of("pobi", "woni", "jun"); + Race race = new Race(carNames); + + race.getCars().get(0).move(); + race.getCars().get(1).move(); + race.getCars().get(1).move(); + race.getCars().get(2).move(); + race.getCars().get(2).move(); + + List winners = race.getWinners(); + assertThat(winners).hasSize(2); + assertThat(winners).extracting("name") + .contains("woni", "jun"); + } } From 890154df12c013fe00043ba95dd1a9b001833cf0 Mon Sep 17 00:00:00 2001 From: cussle Date: Mon, 10 Jun 2024 05:54:08 +0900 Subject: [PATCH 15/16] =?UTF-8?q?refactor:=20Car=20=ED=81=B4=EB=9E=98?= =?UTF-8?q?=EC=8A=A4=20=EA=B0=9C=EC=84=A0=20=EB=B0=8F=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EA=B0=95=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Car 클래스에서 name 필드를 final로 선언하여 불변성을 유지 - 생성자에서 position을 0으로 초기화하여 명확성 향상 - 불필요한 initializePosition 메서드를 제거하여 클래스 간결화 - 테스트 케이스 추가로 정확도 검증의 신뢰성 강화 --- src/main/java/model/Car.java | 7 ++----- src/main/java/view/OutputView.java | 11 ++++------- src/test/java/model/CarTest.java | 6 ++++++ src/test/java/model/RaceTest.java | 16 ++++++++++++++++ 4 files changed, 28 insertions(+), 12 deletions(-) diff --git a/src/main/java/model/Car.java b/src/main/java/model/Car.java index 357e9611..147cbe44 100644 --- a/src/main/java/model/Car.java +++ b/src/main/java/model/Car.java @@ -1,7 +1,7 @@ package model; public class Car { - private String name; + private final String name; private int position; public Car(String name) { @@ -9,6 +9,7 @@ public Car(String name) { throw new IllegalArgumentException("[ERROR] 자동차 이름은 1자 이상 5자 이하만 가능합니다."); } this.name = name; + this.position = 0; // 생성자에서 위치를 초기화 } public String getName() { @@ -19,10 +20,6 @@ public int getPosition() { return position; } - public void initializePosition() { - this.position = 0; - } - public void move() { this.position++; } diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java index 81473812..5b42b341 100644 --- a/src/main/java/view/OutputView.java +++ b/src/main/java/view/OutputView.java @@ -10,13 +10,10 @@ public static void printMessage(String message) { public static void printRaceStatus(List cars) { for (Car car : cars) { - StringBuilder status = new StringBuilder(); - status.append(car.getName()).append(" : "); - for (int i = 0; i < car.getPosition(); i++) { - status.append("-"); - } - System.out.println(status.toString()); + String status = car.getName() + " : " + + "-".repeat(Math.max(0, car.getPosition())); + printMessage(status); } - System.out.println(); // 빈 줄로 회차 구분 + printMessage(""); // 빈 줄로 회차 구분 } } diff --git a/src/test/java/model/CarTest.java b/src/test/java/model/CarTest.java index 0fba60bc..b40b6492 100644 --- a/src/test/java/model/CarTest.java +++ b/src/test/java/model/CarTest.java @@ -51,4 +51,10 @@ public void carShouldMoveMultipleTimes() { car.move(); assertEquals(2, car.getPosition()); } + + @Test + public void carNameShouldAllowSpecialCharacters() { + Car car = new Car("p@bi"); + assertEquals("p@bi", car.getName()); + } } diff --git a/src/test/java/model/RaceTest.java b/src/test/java/model/RaceTest.java index 5c0e73c5..b1fdda12 100644 --- a/src/test/java/model/RaceTest.java +++ b/src/test/java/model/RaceTest.java @@ -66,4 +66,20 @@ public void shouldHaveMultipleWinnersIfTied() { assertThat(winners).extracting("name") .contains("woni", "jun"); } + + @Test + public void shouldHaveSingleWinnerWhenOnlyOneCarHasMaxPosition() { + List carNames = List.of("pobi", "woni", "jun"); + Race race = new Race(carNames); + + race.getCars().get(0).move(); + race.getCars().get(0).move(); // pobi moves twice + race.getCars().get(1).move(); // woni moves once + race.getCars().get(2).move(); + + List winners = race.getWinners(); + assertThat(winners).hasSize(1); + assertThat(winners).extracting("name") + .contains("pobi"); + } } From 83b90742246ebd4a272880c081cab013dcd97463 Mon Sep 17 00:00:00 2001 From: cussle Date: Mon, 10 Jun 2024 06:10:21 +0900 Subject: [PATCH 16/16] =?UTF-8?q?fix:=20=EA=B0=81=20=EB=9D=BC=EC=9A=B4?= =?UTF-8?q?=EB=93=9C=EC=9D=98=20=EC=9E=90=EB=8F=99=EC=B0=A8=20=EC=9C=84?= =?UTF-8?q?=EC=B9=98=20=EC=B6=9C=EB=A0=A5=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Race 클래스의 run 메서드에서 각 라운드가 끝날 때마다 자동차 위치를 출력하도록 OutputView.printRaceStatus 호출 추가 - 자동차 경주 진행 중 각 라운드의 현재 상태를 확인 가능 --- src/main/java/model/Race.java | 25 +++++++++++-------------- src/test/java/model/CarTest.java | 3 --- 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/src/main/java/model/Race.java b/src/main/java/model/Race.java index c1569a66..76a5e802 100644 --- a/src/main/java/model/Race.java +++ b/src/main/java/model/Race.java @@ -2,26 +2,19 @@ import java.util.List; import java.util.Random; -import view.OutputView; import java.util.stream.Collectors; +import view.OutputView; // 추가 public class Race { private final List cars; private static final int FORWARD_THRESHOLD = 4; private static final int RANDOM_BOUND = 10; - private static final Random RANDOM = new Random(); // static final로 변경 + private static final Random RANDOM = new Random(); public Race(List carNames) { this.cars = carNames.stream() .map(Car::new) .collect(Collectors.toList()); - initializeCars(); - } - - private void initializeCars() { - for (Car car : cars) { - car.initializePosition(); - } } public List getCars() { @@ -31,15 +24,19 @@ public List getCars() { public void run(int raceCount) { for (int i = 0; i < raceCount; i++) { moveCars(); - OutputView.printRaceStatus(cars); // 출력 로직을 외부로 이동 + OutputView.printRaceStatus(cars); // 현재 상태 출력 } } private void moveCars() { for (Car car : cars) { - if (RANDOM.nextInt(RANDOM_BOUND) >= FORWARD_THRESHOLD) { - car.move(); - } + moveCar(car); + } + } + + private void moveCar(Car car) { + if (RANDOM.nextInt(RANDOM_BOUND) >= FORWARD_THRESHOLD) { + car.move(); } } @@ -53,4 +50,4 @@ public List getWinners() { .filter(car -> car.getPosition() == maxPosition) .collect(Collectors.toList()); } -} +} \ No newline at end of file diff --git a/src/test/java/model/CarTest.java b/src/test/java/model/CarTest.java index b40b6492..4f789951 100644 --- a/src/test/java/model/CarTest.java +++ b/src/test/java/model/CarTest.java @@ -31,14 +31,12 @@ public void carNameShouldBeSetCorrectly() { @Test public void carShouldStartAtPositionZero() { Car car = new Car("test"); - car.initializePosition(); assertEquals(0, car.getPosition()); } @Test public void carShouldMove() { Car car = new Car("test"); - car.initializePosition(); car.move(); assertEquals(1, car.getPosition()); } @@ -46,7 +44,6 @@ public void carShouldMove() { @Test public void carShouldMoveMultipleTimes() { Car car = new Car("test"); - car.initializePosition(); car.move(); car.move(); assertEquals(2, car.getPosition());