-
Notifications
You must be signed in to change notification settings - Fork 1.3k
[자동차 경주] 육새라 미션 제출합니다. #1462
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
[자동차 경주] 육새라 미션 제출합니다. #1462
Changes from all commits
54f01ef
908e9eb
0fa6ff3
ab349eb
2d17e2b
8751e2e
63fe0f5
1873ed8
0e384d0
1132387
d1b6fde
8d1c51d
91d27a1
baa67e6
0fb5870
bd86c23
89bab0b
299031f
35ccfb1
eab9646
63018e9
5ad4a10
d8b5d85
ac6945d
7f32022
85d9059
2368123
f029f09
ffc75af
c1709e7
9be4d9a
be893ff
c41fe79
49810ac
6c58fcc
bfebb26
c31ff22
cfa2760
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1,47 @@ | ||
| # java-racingcar-precourse | ||
| # 🚗 자동차 경주 🏁 | ||
|
|
||
| ## 기능 명세서 | ||
|
|
||
| > 📝 기능 명세서는 프로그램 실행 흐름에 따라 작성한다.<br/> | ||
| > ☑️ 기능 구현은 주요 기능부터 먼저 구현한다.<br/> | ||
| > ⚠️ 경주는 경쟁 상대가 있어야 하므로 유효한 이름을 2개 이상 입력해야 게임을 진행할 수 있다. | ||
|
|
||
| ### 경주할 자동차들의 이름을 입력받는다. | ||
|
|
||
| - [X] 자동차 이름 입력을 요청하는 메시지를 출력한다. | ||
| - [X] 입력된 문자열을 쉼표(,)를 기준으로 구분해 이름을 추출한다. | ||
| - [X] 잘못된 값을 입력할 경우 `IllegalArgumentException`을 던진다. | ||
| - [X] 이름을 2개 미만으로 입력할 경우 | ||
| - [X] 입력된 이름이 1자 미만, 5자 초과일 경우 | ||
| - [X] 이름이 중복일 경우 | ||
|
|
||
| ### 전진 시도 횟수를 입력받는다. | ||
|
|
||
| - [X] 시도 횟수 입력 안내 메시지를 출력한다. | ||
| - [X] 입력된 문자열을 자연수로 변환한다. | ||
| - [X] 잘못된 값을 입력할 경우 `IllegalArgumentException`을 던진다. | ||
| - [X] 0~9로 이루어진 문자열이 아닌 경우 | ||
| - [X] 1 미만의 수를 입력한 경우 | ||
|
|
||
| ### 자동차 경주를 진행한다. | ||
|
|
||
| - [X] 실행 결과 문구를 출력한다. | ||
| - [X] 사용자가 입력한 시도 횟수만큼 반복한다. | ||
| - [X] 모든 자동차가 전진을 1번 시도한다. | ||
| - [X] 0부터 9까지의 정수 중 무작위 값을 1개 뽑는다. | ||
| - [X] 무작위 값이 4 이상이면 전진한다. | ||
| - [X] 한 라운드 실행 결과를 출력한다. | ||
| - [X] 1줄에 자동차 1개씩 이름과 이동한 횟수를 출력한다. | ||
|
|
||
| ### 게임 종료 후 우승자를 찾는다. | ||
|
|
||
| - [X] 자동차들의 이동 횟수 중 최대값을 찾는다. | ||
| - [X] 이동 횟수의 최대값을 가진 자동차들의 이름을 리턴한다. | ||
|
|
||
| ### 우승자를 알려준다. | ||
|
|
||
| - [X] 우승자 안내 문구를 출력한다. | ||
| - [X] 우승자의 이름을 출력한다. | ||
| - [X] 공동 우승자는 쉼표(,)를 이용하여 구분한다. | ||
|
|
||
| ### `IllegalArgumentException`이 발생하면 애플리케이션을 종료한다. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,12 @@ | ||
| package racingcar; | ||
|
|
||
| import camp.nextstep.edu.missionutils.Console; | ||
| import racingcar.config.AppConfig; | ||
|
|
||
| public class Application { | ||
| public static void main(String[] args) { | ||
| // TODO: 프로그램 구현 | ||
| AppConfig appConfig = new AppConfig(); | ||
| appConfig.run(); | ||
| Console.close(); | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| package racingcar.View; | ||
|
|
||
| import static racingcar.View.ViewConstants.TOTAL_ROUNDS_REGEX; | ||
| import static racingcar.View.ViewConstants.CONTAINS_NON_DIGIT_ERROR_MESSAGE; | ||
|
|
||
| public class InputValidator { | ||
| public void validateThatContainsOnlyDigits(String input) { | ||
| if (!input.matches(TOTAL_ROUNDS_REGEX)) { | ||
| throw new IllegalArgumentException(CONTAINS_NON_DIGIT_ERROR_MESSAGE); | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| package racingcar.View; | ||
|
|
||
| import static racingcar.View.ViewConstants.ENTER_CAR_NAMES_MESSAGE; | ||
| import static racingcar.View.ViewConstants.ENTER_TOTAL_ROUNDS_MESSAGE; | ||
|
|
||
| import camp.nextstep.edu.missionutils.Console; | ||
|
|
||
| public class InputView { | ||
| public String requestCarNames() { | ||
| System.out.println(ENTER_CAR_NAMES_MESSAGE); | ||
| return Console.readLine(); | ||
| } | ||
|
|
||
| public String requestTotalRounds() { | ||
| System.out.println(ENTER_TOTAL_ROUNDS_MESSAGE); | ||
| return Console.readLine(); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| package racingcar.View; | ||
|
|
||
| import static racingcar.View.ViewConstants.NAME_DELIMITER; | ||
| import static racingcar.View.ViewConstants.RACE_RESULT; | ||
| import static racingcar.View.ViewConstants.WHITE_SPACE; | ||
| import static racingcar.View.ViewConstants.WINNER_IS; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| public class OutputView { | ||
| public void printResultPhrase() { | ||
| System.out.println(System.lineSeparator() + RACE_RESULT); | ||
| } | ||
|
|
||
| public void printRaceProgress(String roundResult) { | ||
| System.out.println(roundResult); | ||
| } | ||
|
|
||
| public void printWinners(List<String> winnerNames) { | ||
| String winners = String.join(NAME_DELIMITER + WHITE_SPACE, winnerNames); | ||
| System.out.printf("%s%s", WINNER_IS, winners); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| package racingcar.View; | ||
|
|
||
| import static racingcar.model.RacingConstants.MAX_LENGTH_OF_CAR_NAME; | ||
| import static racingcar.model.RacingConstants.MIN_LENGTH_OF_CAR_NAME; | ||
| import static racingcar.model.RacingConstants.MIN_ROUNDS; | ||
| import static racingcar.model.RacingConstants.REQUIRED_MIN_PLAYERS; | ||
|
|
||
| public class ViewConstants { | ||
| public static final String ENTER_CAR_NAMES_MESSAGE = "경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)"; | ||
| public static final String ENTER_TOTAL_ROUNDS_MESSAGE = "시도할 횟수는 몇 회인가요?"; | ||
|
|
||
| public static final String NAME_DELIMITER = ","; | ||
| public static final String TOTAL_ROUNDS_REGEX = "^[0-9]+$"; | ||
|
|
||
| public static final String RACE_RESULT = "실행 결과"; | ||
| public static final String RACE_DISPLAY_FORMAT = "%s : "; | ||
| public static final String MOVE_SYMBOL = "-"; | ||
|
|
||
| public static final String WINNER_IS = "최종 우승자 : "; | ||
| public static final String WHITE_SPACE = " "; | ||
|
|
||
| public static final String NOT_ENOUGH_PLAYERS_ERROR_MESSAGE | ||
| = String.format("이름을 %s개 이상 입력해야 게임이 시작됩니다.", REQUIRED_MIN_PLAYERS); | ||
| public static final String NAME_LENGTH_ERROR_MESSAGE | ||
| = String.format("이름은 %s자 이상, %s자 이하만 가능합니다.", MIN_LENGTH_OF_CAR_NAME, MAX_LENGTH_OF_CAR_NAME); | ||
| public static final String DUPLICATE_NAME_ERROR_MESSAGE = "중복된 이름은 사용할 수 없습니다."; | ||
|
|
||
| public static final String CONTAINS_NON_DIGIT_ERROR_MESSAGE = "시도 횟수는 숫자만 입력해 주세요."; | ||
| public static final String LESS_THAN_MIN_ROUNDS_ERROR_MESSAGE | ||
| = String.format("시도 횟수는 %s회 이상이어야 합니다.", MIN_ROUNDS); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| package racingcar.config; | ||
|
|
||
| import racingcar.View.InputValidator; | ||
| import racingcar.model.CarRacing; | ||
| import racingcar.controller.CarRacingController; | ||
| import racingcar.model.Racers; | ||
| import racingcar.model.RacingRule; | ||
| import racingcar.View.InputView; | ||
| import racingcar.View.OutputView; | ||
| import racingcar.service.CarRacingService; | ||
|
|
||
| public class AppConfig { | ||
| public InputView inputView() { | ||
| return new InputView(); | ||
| } | ||
|
Comment on lines
+13
to
+15
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 호출할 때 마다 인스턴스를 새로 생성하는데 매번 새로운 인스턴스를 만드는게 좋은 설계인지 고민을 해보셨으면 좋겠습니다. |
||
|
|
||
| public InputValidator inputValidator() { | ||
| return new InputValidator(); | ||
| } | ||
|
|
||
| public OutputView outputView() { | ||
| return new OutputView(); | ||
| } | ||
|
|
||
| public CarRacingController carRacingController() { | ||
| return new CarRacingController(inputView(), inputValidator(), outputView()); | ||
| } | ||
|
|
||
| public CarRacingService carRacingService() { | ||
| return new CarRacingService(); | ||
| } | ||
|
|
||
| public RacingRule racingRule() { | ||
| return carRacingService().setRacingRule(); | ||
| } | ||
|
|
||
| public Racers racers() { | ||
| return carRacingService().registerRacers(carRacingController().extractCarNames()); | ||
| } | ||
|
|
||
| public int totalRounds() { | ||
| return carRacingService().registerTotalRounds(carRacingController().convertToNumber()); | ||
| } | ||
|
Comment on lines
+29
to
+43
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. CarRacingService의 인스턴스가 매번 새로 생성되어 비효율적이라고 생각됩니다. |
||
|
|
||
| public CarRacing carRacing() { | ||
| return new CarRacing(racingRule(), racers()); | ||
| } | ||
|
|
||
| public void run() { | ||
| CarRacingController carRacingController = carRacingController(); | ||
| CarRacing carRacing = carRacing(); | ||
| carRacingController.deliverRaceProgress(carRacing, totalRounds()); | ||
| carRacingController.deliverWinners(carRacing); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| package racingcar.controller; | ||
|
|
||
| import static racingcar.View.ViewConstants.NAME_DELIMITER; | ||
|
|
||
| import java.util.Arrays; | ||
| import java.util.List; | ||
| import racingcar.View.InputView; | ||
| import racingcar.View.OutputView; | ||
| import racingcar.View.InputValidator; | ||
| import racingcar.model.CarRacing; | ||
|
|
||
| public class CarRacingController { | ||
| private final InputView inputView; | ||
| private final InputValidator inputValidator; | ||
| private final OutputView outputView; | ||
|
|
||
| public CarRacingController(InputView inputView, InputValidator inputValidator, OutputView outputView) { | ||
| this.inputView = inputView; | ||
| this.inputValidator = inputValidator; | ||
| this.outputView = outputView; | ||
| } | ||
|
|
||
| public List<String> extractCarNames() { | ||
| String input = inputView.requestCarNames(); | ||
| return splitByDelimiter(input); | ||
| } | ||
|
|
||
| private List<String> splitByDelimiter(String input) { | ||
| return Arrays.stream(input.split(NAME_DELIMITER)).toList(); | ||
| } | ||
|
|
||
| public int convertToNumber() { | ||
| String input = inputView.requestTotalRounds(); | ||
| inputValidator.validateThatContainsOnlyDigits(input); | ||
| return Integer.parseInt(input); | ||
| } | ||
|
|
||
| public void deliverRaceProgress(CarRacing carRacing, int totalRounds) { | ||
| outputView.printResultPhrase(); | ||
| for (int i = 0; i < totalRounds; i++) { | ||
| List<String> currentResults = carRacing.playARound(); | ||
| outputView.printRaceProgress(convertToString(currentResults)); | ||
| } | ||
| } | ||
|
|
||
| private String convertToString(List<String> currentResults) { | ||
| StringBuilder output = new StringBuilder(); | ||
| for (String result : currentResults) { | ||
| output.append(result).append(System.lineSeparator()); | ||
| } | ||
| return output.toString(); | ||
| } | ||
|
|
||
| public void deliverWinners(CarRacing carRacing) { | ||
| outputView.printWinners(carRacing.announceWinners()); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| package racingcar.model; | ||
|
|
||
| import static racingcar.model.RacingConstants.MAX_LENGTH_OF_CAR_NAME; | ||
| import static racingcar.View.ViewConstants.MOVE_SYMBOL; | ||
| import static racingcar.View.ViewConstants.NAME_LENGTH_ERROR_MESSAGE; | ||
| import static racingcar.View.ViewConstants.RACE_DISPLAY_FORMAT; | ||
|
|
||
| public class Car implements Comparable<Car> { | ||
| private final String name; | ||
| private int moveCount; | ||
|
|
||
| public Car(String name, int moveCount) { | ||
| validateLengthOf(name); | ||
| this.name = name; | ||
| this.moveCount = moveCount; | ||
| } | ||
|
|
||
| private void validateLengthOf(String name) { | ||
| if (name == null || name.isBlank() || (name.length() > MAX_LENGTH_OF_CAR_NAME)) { | ||
| throw new IllegalArgumentException(NAME_LENGTH_ERROR_MESSAGE); | ||
| } | ||
| } | ||
|
|
||
| public int moveForwardIf(boolean possible) { | ||
| if (possible) { | ||
| moveCount++; | ||
| return moveCount; | ||
| } | ||
| return -1; | ||
| } | ||
|
|
||
| public boolean isSameMoveCount(Car other) { | ||
| return other.moveCount == this.moveCount; | ||
| } | ||
|
|
||
| @Override | ||
| public int compareTo(Car other) { | ||
| return this.moveCount - other.moveCount; | ||
| } | ||
|
|
||
| public String getStatus() { | ||
| return String.format(RACE_DISPLAY_FORMAT, name).concat(MOVE_SYMBOL.repeat(moveCount)); | ||
| } | ||
|
|
||
| public String getName() { | ||
| return name; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| package racingcar.model; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| public class CarRacing { | ||
| private final RacingRule racingRule; | ||
| private final Racers racers; | ||
|
|
||
| public CarRacing(RacingRule racingRule, Racers racers) { | ||
| this.racingRule = racingRule; | ||
| this.racers = racers; | ||
| } | ||
|
|
||
| public List<String> playARound() { | ||
| racers.tryToMoveWith(racingRule); | ||
| return racers.getCurrentResult(); | ||
| } | ||
|
|
||
| public List<String> announceWinners() { | ||
| return racers.getWinnerNames(); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
중요한건 아니지만
와 같이 작성하는 방법도 있습니다.