From 93c6640ec7696f35e94b4ed5a13289911d0adc23 Mon Sep 17 00:00:00 2001 From: YoujeongChoi Date: Wed, 1 Nov 2023 23:44:43 +0900 Subject: [PATCH 1/6] =?UTF-8?q?feat:=20=EC=82=AC=EC=9A=A9=EC=9E=90?= =?UTF-8?q?=EB=A1=9C=EB=B6=80=ED=84=B0=20=EC=B0=A8=EB=9F=89=20=EC=9D=B4?= =?UTF-8?q?=EB=A6=84=20=EC=9E=85=EB=A0=A5=EB=B0=9B=EB=8A=94=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/racingcar/Application.java | 5 ++- .../java/racingcar/domain/GameManager.java | 34 +++++++++++++++++++ src/main/java/racingcar/domain/RacingCar.java | 15 ++++++++ .../racingcar/domain/RacingCarManager.java | 17 ++++++++++ .../racingcar/util/RacingCarConstant.java | 8 +++++ .../java/racingcar/util/UserInputUtil.java | 33 ++++++++++++++++++ 6 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 src/main/java/racingcar/domain/GameManager.java create mode 100644 src/main/java/racingcar/domain/RacingCar.java create mode 100644 src/main/java/racingcar/domain/RacingCarManager.java create mode 100644 src/main/java/racingcar/util/RacingCarConstant.java create mode 100644 src/main/java/racingcar/util/UserInputUtil.java diff --git a/src/main/java/racingcar/Application.java b/src/main/java/racingcar/Application.java index a17a52e7242..35beb5dee25 100644 --- a/src/main/java/racingcar/Application.java +++ b/src/main/java/racingcar/Application.java @@ -1,7 +1,10 @@ package racingcar; +import racingcar.domain.GameManager; + public class Application { public static void main(String[] args) { - // TODO: 프로그램 구현 + GameManager gameManager = new GameManager(); + gameManager.startGame(); } } diff --git a/src/main/java/racingcar/domain/GameManager.java b/src/main/java/racingcar/domain/GameManager.java new file mode 100644 index 00000000000..c3ad35720a4 --- /dev/null +++ b/src/main/java/racingcar/domain/GameManager.java @@ -0,0 +1,34 @@ +package racingcar.domain; + +import org.assertj.core.error.uri.ShouldHaveUserInfo; +import racingcar.util.UserInputUtil; + +import java.util.List; + +/* +* 게임 실행 +* */ +public class GameManager { + + private RacingCarManager racingCarManager; + private int tries; + + public void startGame() throws IllegalArgumentException { + initializeGame(); + playGame(); + endGame(); + } + + private void initializeGame() { + List carNameList = UserInputUtil.getCarNames(); + + } + + private void playGame() { + + } + + private void endGame() { + + } +} diff --git a/src/main/java/racingcar/domain/RacingCar.java b/src/main/java/racingcar/domain/RacingCar.java new file mode 100644 index 00000000000..9af8f8eb368 --- /dev/null +++ b/src/main/java/racingcar/domain/RacingCar.java @@ -0,0 +1,15 @@ +package racingcar.domain; + +public class RacingCar { + + private final String name; + private int position = 0; + + public RacingCar(String name) { + this.name = name; + } + + public String getName() { + return name; + } +} diff --git a/src/main/java/racingcar/domain/RacingCarManager.java b/src/main/java/racingcar/domain/RacingCarManager.java new file mode 100644 index 00000000000..87af79d1bc6 --- /dev/null +++ b/src/main/java/racingcar/domain/RacingCarManager.java @@ -0,0 +1,17 @@ +package racingcar.domain; + +import java.util.ArrayList; +import java.util.List; + +public class RacingCarManager { + + private List cars; + + public RacingCarManager(List carNames) { + this.cars = new ArrayList<>(); + + for (String carName: carNames) { + cars.add(new RacingCar(carName)); + } + } +} diff --git a/src/main/java/racingcar/util/RacingCarConstant.java b/src/main/java/racingcar/util/RacingCarConstant.java new file mode 100644 index 00000000000..77a9877fe65 --- /dev/null +++ b/src/main/java/racingcar/util/RacingCarConstant.java @@ -0,0 +1,8 @@ +package racingcar.util; + +public class RacingCarConstant { + + public static final String INPUT_CAR_NAMES_MESSAGE = "경주할 자동차 이름을 입력하세요. (이름은 쉼표(,) 기준으로 구분)"; + public static final String INVALID_CAR_NAMES_INPUT = "자동차 이름 입력이 올바르지 않습니다. 쉼표로 구분해주세요."; + +} diff --git a/src/main/java/racingcar/util/UserInputUtil.java b/src/main/java/racingcar/util/UserInputUtil.java new file mode 100644 index 00000000000..3894d7c50ef --- /dev/null +++ b/src/main/java/racingcar/util/UserInputUtil.java @@ -0,0 +1,33 @@ +package racingcar.util; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import camp.nextstep.edu.missionutils.Console; + + +public class UserInputUtil { + + public static List getCarNames() { + System.out.println(RacingCarConstant.INPUT_CAR_NAMES_MESSAGE); + String input = Console.readLine(); + validateCarNamesInput(input); + List carNames = Arrays.stream(input.split(",")) + .map(String::trim) + .collect(Collectors.toList()); + return carNames; + } + + private static void validateCarNamesInput(String input) { + if (input.startsWith(",") || input.endsWith(",") || input.contains(",,") || input.trim().isEmpty()) { + throw new IllegalArgumentException(RacingCarConstant.INVALID_CAR_NAMES_INPUT); + } + String[] names = input.split(","); + for (String name : names) { + if (name.trim().isEmpty()) { + throw new IllegalArgumentException(RacingCarConstant.INVALID_CAR_NAMES_INPUT); + } + } + } +} From ab84a6949b577a6ea0be16a42f334697de83bfec Mon Sep 17 00:00:00 2001 From: YoujeongChoi Date: Wed, 1 Nov 2023 23:47:18 +0900 Subject: [PATCH 2/6] =?UTF-8?q?feat:=20=EC=82=AC=EC=9A=A9=EC=9E=90?= =?UTF-8?q?=EB=A1=9C=EB=B6=80=ED=84=B0=20=EC=8B=9C=EB=8F=84=ED=95=A0=20?= =?UTF-8?q?=ED=9A=9F=EC=88=98=EB=A5=BC=20=EC=9E=85=EB=A0=A5=EB=B0=9B?= =?UTF-8?q?=EA=B3=A0=20=EA=B2=80=EC=A6=9D=ED=95=98=EB=8A=94=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/racingcar/domain/GameManager.java | 3 ++- src/main/java/racingcar/util/RacingCarConstant.java | 2 ++ src/main/java/racingcar/util/UserInputUtil.java | 10 ++++++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/main/java/racingcar/domain/GameManager.java b/src/main/java/racingcar/domain/GameManager.java index c3ad35720a4..5234dcb7bbe 100644 --- a/src/main/java/racingcar/domain/GameManager.java +++ b/src/main/java/racingcar/domain/GameManager.java @@ -21,7 +21,8 @@ public void startGame() throws IllegalArgumentException { private void initializeGame() { List carNameList = UserInputUtil.getCarNames(); - + this.tries = UserInputUtil.getTries(); + this.racingCarManager = new RacingCarManager(carNameList); } private void playGame() { diff --git a/src/main/java/racingcar/util/RacingCarConstant.java b/src/main/java/racingcar/util/RacingCarConstant.java index 77a9877fe65..a1be5335372 100644 --- a/src/main/java/racingcar/util/RacingCarConstant.java +++ b/src/main/java/racingcar/util/RacingCarConstant.java @@ -4,5 +4,7 @@ public class RacingCarConstant { public static final String INPUT_CAR_NAMES_MESSAGE = "경주할 자동차 이름을 입력하세요. (이름은 쉼표(,) 기준으로 구분)"; public static final String INVALID_CAR_NAMES_INPUT = "자동차 이름 입력이 올바르지 않습니다. 쉼표로 구분해주세요."; + public static final String INPUT_TRIES_MESSAGE = "시도할 횟수는 몇 회인가요?"; + public static final String NUMBER_INPUT_ERROR_MESSAGE = "숫자를 입력해주세요."; } diff --git a/src/main/java/racingcar/util/UserInputUtil.java b/src/main/java/racingcar/util/UserInputUtil.java index 3894d7c50ef..a95b017afb8 100644 --- a/src/main/java/racingcar/util/UserInputUtil.java +++ b/src/main/java/racingcar/util/UserInputUtil.java @@ -30,4 +30,14 @@ private static void validateCarNamesInput(String input) { } } } + + public static int getTries() { + System.out.println(RacingCarConstant.INPUT_TRIES_MESSAGE); + try { + return Integer.parseInt(Console.readLine()); + } catch (NumberFormatException e) { + System.out.println(RacingCarConstant.NUMBER_INPUT_ERROR_MESSAGE); + return getTries(); + } + } } From bc3ab60b11d9c46b45100b1165293d4c17e3b57f Mon Sep 17 00:00:00 2001 From: YoujeongChoi Date: Wed, 1 Nov 2023 23:51:26 +0900 Subject: [PATCH 3/6] =?UTF-8?q?feat:=20=EC=B0=A8=20=EA=B0=9D=EC=B2=B4?= =?UTF-8?q?=EB=A5=BC=20=EC=9C=84=ED=95=9C=20=ED=81=B4=EB=9E=98=EC=8A=A4=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20=EB=B0=8F=20=EC=A0=84=EC=A7=84=20=EB=98=90?= =?UTF-8?q?=EB=8A=94=20=EB=A9=88=EC=B6=A4=20=EB=A1=9C=EC=A7=81=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 --- src/main/java/racingcar/domain/RacingCar.java | 10 ++++++++++ src/main/java/racingcar/domain/RandomNumGenerator.java | 9 +++++++++ src/main/java/racingcar/util/RacingCarConstant.java | 7 +++++-- 3 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 src/main/java/racingcar/domain/RandomNumGenerator.java diff --git a/src/main/java/racingcar/domain/RacingCar.java b/src/main/java/racingcar/domain/RacingCar.java index 9af8f8eb368..42f6092386f 100644 --- a/src/main/java/racingcar/domain/RacingCar.java +++ b/src/main/java/racingcar/domain/RacingCar.java @@ -8,8 +8,18 @@ public class RacingCar { public RacingCar(String name) { this.name = name; } + public void move() { + int randomValue = RandomNumGenerator.generate(); + if (randomValue >= RacingCarConstant.MOVE_THRESHOLD) { + position++; + } + } public String getName() { return name; } + + public int getPosition() { + return position; + } } diff --git a/src/main/java/racingcar/domain/RandomNumGenerator.java b/src/main/java/racingcar/domain/RandomNumGenerator.java new file mode 100644 index 00000000000..923e32b416f --- /dev/null +++ b/src/main/java/racingcar/domain/RandomNumGenerator.java @@ -0,0 +1,9 @@ +package racingcar.domain; + +import camp.nextstep.edu.missionutils.Randoms; + +public class RandomNumGenerator { + public static int generate() { + return Randoms.pickNumberInRange(0, 9); + } +} diff --git a/src/main/java/racingcar/util/RacingCarConstant.java b/src/main/java/racingcar/util/RacingCarConstant.java index a1be5335372..6a7e92b5e15 100644 --- a/src/main/java/racingcar/util/RacingCarConstant.java +++ b/src/main/java/racingcar/util/RacingCarConstant.java @@ -3,8 +3,11 @@ public class RacingCarConstant { public static final String INPUT_CAR_NAMES_MESSAGE = "경주할 자동차 이름을 입력하세요. (이름은 쉼표(,) 기준으로 구분)"; - public static final String INVALID_CAR_NAMES_INPUT = "자동차 이름 입력이 올바르지 않습니다. 쉼표로 구분해주세요."; public static final String INPUT_TRIES_MESSAGE = "시도할 횟수는 몇 회인가요?"; + public static final String RESULT_HEADER_MESSAGE = "실행 결과"; public static final String NUMBER_INPUT_ERROR_MESSAGE = "숫자를 입력해주세요."; - + public static final String INVALID_CAR_NAMES_INPUT = "자동차 이름 입력이 올바르지 않습니다. 쉼표로 구분해주세요."; + public static final String CAR_NAME_LENGTH_ERROR_MESSAGE = "자동차 이름은 5자 이하로 입력해주세요."; + public static final int CAR_NAME_MAX_LENGTH = 5; + public static final int MOVE_THRESHOLD = 4; } From cb44e4afc640b03f594bdf8354efd4c7c4915864 Mon Sep 17 00:00:00 2001 From: YoujeongChoi Date: Wed, 1 Nov 2023 23:55:22 +0900 Subject: [PATCH 4/6] =?UTF-8?q?Feat:=20=EA=B2=8C=EC=9E=84=20=EC=8B=A4?= =?UTF-8?q?=ED=96=89=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/racingcar/domain/GameManager.java | 11 +++++++++-- .../racingcar/domain/RacingCarManager.java | 18 ++++++++++++++++-- .../domain/RacingCarRankingManager.java | 18 ++++++++++++++++++ src/main/java/racingcar/util/GamePrinter.java | 18 ++++++++++++++++++ 4 files changed, 61 insertions(+), 4 deletions(-) create mode 100644 src/main/java/racingcar/domain/RacingCarRankingManager.java create mode 100644 src/main/java/racingcar/util/GamePrinter.java diff --git a/src/main/java/racingcar/domain/GameManager.java b/src/main/java/racingcar/domain/GameManager.java index 5234dcb7bbe..fb5eaf568d9 100644 --- a/src/main/java/racingcar/domain/GameManager.java +++ b/src/main/java/racingcar/domain/GameManager.java @@ -1,6 +1,8 @@ package racingcar.domain; import org.assertj.core.error.uri.ShouldHaveUserInfo; +import racingcar.util.GamePrinter; +import racingcar.util.RacingCarConstant; import racingcar.util.UserInputUtil; import java.util.List; @@ -26,10 +28,15 @@ private void initializeGame() { } private void playGame() { - + System.out.println(RacingCarConstant.RESULT_HEADER_MESSAGE); + for (int i = 0; i < tries; i++) { + racingCarManager.playOneRound(); + GamePrinter.printRoundResult(racingCarManager.getCars()); + } } private void endGame() { - + List winners = racingCarManager.findWinners(); + GamePrinter.printWinners(winners); } } diff --git a/src/main/java/racingcar/domain/RacingCarManager.java b/src/main/java/racingcar/domain/RacingCarManager.java index 87af79d1bc6..d41056a1e0d 100644 --- a/src/main/java/racingcar/domain/RacingCarManager.java +++ b/src/main/java/racingcar/domain/RacingCarManager.java @@ -9,9 +9,23 @@ public class RacingCarManager { public RacingCarManager(List carNames) { this.cars = new ArrayList<>(); - - for (String carName: carNames) { + for (String carName : carNames) { cars.add(new RacingCar(carName)); } } + + public void playOneRound() { + for (RacingCar car : cars) { + car.move(); + } + } + + public List getCars() { + return cars; + } + + public List findWinners() { + RacingCarRankingManager rankingManager = new RacingCarRankingManager(); + return rankingManager.getWinners(cars); + } } diff --git a/src/main/java/racingcar/domain/RacingCarRankingManager.java b/src/main/java/racingcar/domain/RacingCarRankingManager.java new file mode 100644 index 00000000000..6c872826cf7 --- /dev/null +++ b/src/main/java/racingcar/domain/RacingCarRankingManager.java @@ -0,0 +1,18 @@ +package racingcar.domain; + +import java.util.List; +import java.util.stream.Collectors; + +public class RacingCarRankingManager { + public List getWinners(List cars) { + int maxPosition = cars.stream() + .mapToInt(RacingCar::getPosition) + .max() + .orElse(0); + + return cars.stream() + .filter(car -> car.getPosition() == maxPosition) + .map(RacingCar::getName) + .collect(Collectors.toList()); + } +} diff --git a/src/main/java/racingcar/util/GamePrinter.java b/src/main/java/racingcar/util/GamePrinter.java new file mode 100644 index 00000000000..ffeaa188aea --- /dev/null +++ b/src/main/java/racingcar/util/GamePrinter.java @@ -0,0 +1,18 @@ +package racingcar.util; + +import racingcar.domain.RacingCar; + +import java.util.List; + +public class GamePrinter { + public static void printRoundResult(List cars) { + for (RacingCar car : cars) { + System.out.println(car.getName() + " : " + "-".repeat(car.getPosition())); + } + System.out.println(); + } + + public static void printWinners(List winners) { + System.out.println("최종 우승자 : " + String.join(", ", winners)); + } +} From ab1c47306fa106f740a5bec80e497d681f44af83 Mon Sep 17 00:00:00 2001 From: YoujeongChoi Date: Wed, 1 Nov 2023 23:56:42 +0900 Subject: [PATCH 5/6] =?UTF-8?q?Fix:=20=EB=B2=84=EA=B7=B8=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/racingcar/domain/RacingCar.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/racingcar/domain/RacingCar.java b/src/main/java/racingcar/domain/RacingCar.java index 42f6092386f..a39194c240b 100644 --- a/src/main/java/racingcar/domain/RacingCar.java +++ b/src/main/java/racingcar/domain/RacingCar.java @@ -1,12 +1,18 @@ package racingcar.domain; +import racingcar.util.RacingCarConstant; + public class RacingCar { private final String name; private int position = 0; public RacingCar(String name) { - this.name = name; + String trimmedName = name.trim(); + if (trimmedName.length() > RacingCarConstant.CAR_NAME_MAX_LENGTH || trimmedName.isEmpty()) { + throw new IllegalArgumentException(RacingCarConstant.CAR_NAME_LENGTH_ERROR_MESSAGE); + } + this.name = trimmedName; } public void move() { int randomValue = RandomNumGenerator.generate(); From dbc9971119f6ab62d1f63fabeb63f52dcca77d98 Mon Sep 17 00:00:00 2001 From: Youjeong Date: Wed, 1 Nov 2023 23:57:50 +0900 Subject: [PATCH 6/6] =?UTF-8?q?docs:=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20=EB=AA=A9=EB=A1=9D=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 | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/docs/README.md b/docs/README.md index e69de29bb2d..2561497f5aa 100644 --- a/docs/README.md +++ b/docs/README.md @@ -0,0 +1,21 @@ +작성한 코드를 통해 여러 가지 중요한 프로그래밍 원칙과 디자인 패턴에 대해 배우고 고민할 수 있었습니다. +### 1. **책임의 분리(Separation of Concerns)**: +- **Domain Logic의 분리**: `RacingCar`, `RacingCarManager`, `GameManager`와 같은 클래스들은 레이싱 게임의 도메인 로직을 담당합니다. 이들은 게임의 상태를 관리하고 게임 규칙을 실행하는 책임을 갖습니다. +- **Input/Output 처리의 분리**: `UserInputUtil`과 `GamePrinter` 클래스는 사용자 입력과 게임 결과의 출력을 담당합니다. 이렇게 함으로써 도메인 로직과 UI 로직을 분리하여 각각의 변경에 덜 영향을 받게 만들었습니다. + +### 2. **재사용성과 유지보수성 향상**: +- **Utility 클래스의 사용**: `UserInputUtil`과 같은 유틸리티 클래스를 사용함으로써, 사용자 입력 처리 로직을 한 곳에 모아 관리할 수 있게 되었습니다. 이는 코드의 중복을 줄이고 재사용성을 향상시켰습니다. +- **상수의 중앙 관리**: `RacingCarConstant` 클래스를 통해 상수 값을 한 곳에서 관리하게 되어, 값을 변경할 때 한 곳만 수정하면 되므로 유지보수성이 향상되었습니다. + +### 3. **객체 지향 프로그래밍(OOP) 원칙 적용**: +- **Encapsulation(캡슐화)**: 각 클래스는 자신의 상태를 적젹적으로 숨기고, 상태를 변경할 수 있는 메서드를 제공합니다. 예를 들어, `RacingCar` 클래스는 `position` 상태를 외부에 직접 노출하지 않고, `move()` 메서드를 통해 상태를 변경합니다. +- **Single Responsibility Principle (SRP, 단일 책임 원칙)**: 각 클래스는 하나의 책임만을 갖습니다. 예를 들어, `UserInputUtil` 클래스는 사용자 입력을 처리하는 책임만을 갖고, `RacingCarManager` 클래스는 게임 로직을 실행하는 책임만을 갖습니다. + +### 4. **유연성과 확장성 고려**: +- **Strategy Pattern의 적용**: 랜덤 값을 생성하는 로직을 `RandomNumGenerator` 클래스로 분리하여, 필요할 경우 다른 랜덤 값 생성 전략으로 쉽게 교체할 수 있게 설계하였습니다. +- **상속/인터페이스 사용 고려**: 현재는 간단한 애플리케이션으로 인터페이스나 추상 클래스를 사용할 필요가 없지만, 게임의 규모가 커지고 다양한 타입의 레이싱 게임을 지원해야 한다면 인터페이스나 추상 클래스를 통해 확장성을 높일 수 있습니다. + +### 5. **Stream API의 활용**: +- Java 8의 Stream API를 사용하여 컬렉션 처리 로직을 간결하고 선언적으로 작성할 수 있었습니다. 이는 코드의 가독성을 향상시켜주고, 버그의 가능성을 줄여줍니다. + +이러한 원칙과 패턴들을 적용함으로써, 코드의 유지보수성, 확장성, 가독성을 향상시키고 버그 발생 가능성을 줄일 수 있었습니다. \ No newline at end of file