Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
54f01ef
docs(README.md): 기능 목록을 작성하라
saera-yook Oct 25, 2024
908e9eb
feat(racingcar): 0에서 9 사이의 숫자 1개를 무작위로 뽑아라
saera-yook Oct 25, 2024
0fa6ff3
feat(racingcar): 자동차가 무작위 값을 전달받아 4 이상이면 위치값이 1 증가하라
saera-yook Oct 25, 2024
ab349eb
feat(racingcar): 자동차의 이름과 현재 위치를 출력하라
saera-yook Oct 25, 2024
2d17e2b
feat(racingcar): 여러대의 자동차가 경주 한 라운드를 진행하라
saera-yook Oct 25, 2024
8751e2e
feat(racingcar): 자동차 경주 한 라운드 실행 결과를 출력하라
saera-yook Oct 25, 2024
63fe0f5
feat(racingcar): 자동차 경주를 사용자가 입력한 시도 횟수만큼 진행하라
saera-yook Oct 25, 2024
1873ed8
feat(racingcar): 실행 결과 문구를 출력하라
saera-yook Oct 25, 2024
0e384d0
docs(README.md): 기능 체크리스트를 업데이트하라
saera-yook Oct 27, 2024
1132387
feat(Judge): 자동차들의 현재 위치값 중 최대값을 찾아라
saera-yook Oct 27, 2024
d1b6fde
feat(Judge): 최대위치값을 가진 자동차들의 이름을 리턴하라
saera-yook Oct 27, 2024
8d1c51d
feat(racingcar): 우승자들의 이름을 출력하라
saera-yook Oct 27, 2024
91d27a1
feat(racingcar): 공동 우승자들의 이름은 쉼표(,)로 구분하여 출력하라
saera-yook Oct 27, 2024
baa67e6
feat(racingcar): 우승자 안내 문구를 출력하라
saera-yook Oct 27, 2024
0fb5870
feat(racingcar): 자동차 이름 입력 안내 메시지를 출력하라
saera-yook Oct 27, 2024
bd86c23
feat(racingcar): 입력된 문자열에서 자동차 이름을 추출하라
saera-yook Oct 27, 2024
89bab0b
feat(racingcar): 입력된 이름이 5자가 넘으면 예외를 던져라
saera-yook Oct 27, 2024
299031f
feat(racingcar): 입력된 이름이 2개 미만이면 예외를 던져라
saera-yook Oct 27, 2024
35ccfb1
feat(racingcar): 입력된 이름에 중복이 있으면 예외를 던져라
saera-yook Oct 27, 2024
eab9646
feat(racingcar): 시도 횟수 입력값에 숫자가 아닌 문자가 있으면 예외를 던져라
saera-yook Oct 27, 2024
63018e9
feat(racingcar): 시도 횟수가 1회 미만이면 예외를 던져라
saera-yook Oct 27, 2024
5ad4a10
fix: 숫자만 있는 문자열 검증 메서드 오류를 고쳐라
saera-yook Oct 27, 2024
d8b5d85
refactor: 입력값을 적절한 자료형으로 바꾸는 역할을 분리하라
saera-yook Oct 27, 2024
ac6945d
refactor: 메서드명과 변수명을 의도에 맞게 개선하라
saera-yook Oct 27, 2024
7f32022
refactor: 이동 가능한지 판단하는 책임을 Move에게 넘겨라
saera-yook Oct 27, 2024
85d9059
fix: 이름 구분자만 입력해도 예외가 발생하지 않는 오류를 고쳐라
saera-yook Oct 27, 2024
2368123
fix: 1자 미만의 이름에 예외가 발생하지 않는 오류를 고쳐라
saera-yook Oct 27, 2024
f029f09
docs: README.md를 업데이트하라
saera-yook Oct 27, 2024
ffc75af
refactor: 상태와 행위를 한 곳에서 관리하도록 책임을 재분배하라
saera-yook Oct 28, 2024
c1709e7
refactor: 경주 규칙과 관련된 책임을 한 곳으로 모아라
saera-yook Oct 28, 2024
9be4d9a
refactor: Application.main()의 역할을 작게 만들어라
saera-yook Oct 28, 2024
be893ff
refactor: 클래스의 역할에 맞게 패키지를 변경하라
saera-yook Oct 28, 2024
c41fe79
refactor: 프로그램 작동 순서 오류를 고쳐라
saera-yook Oct 28, 2024
49810ac
refactor: 비즈니스 로직과 UI 로직을 분리하라
saera-yook Oct 28, 2024
6c58fcc
refactor: domain 클래스들을 model 패키지로 변경하라
saera-yook Oct 28, 2024
bfebb26
test: Racers의 메서드들을 테스트하라
saera-yook Oct 28, 2024
c31ff22
test: 최소 시도 횟수 이상인지 검증하는 메서드를 테스트하라
saera-yook Oct 28, 2024
cfa2760
refactor: 테스트 클래스들의 잘못된 패키지를 변경하라
saera-yook Oct 28, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 47 additions & 1 deletion README.md
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`이 발생하면 애플리케이션을 종료한다.
9 changes: 7 additions & 2 deletions src/main/java/racingcar/Application.java
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();
}
}
}
12 changes: 12 additions & 0 deletions src/main/java/racingcar/View/InputValidator.java
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);
}
}
}
18 changes: 18 additions & 0 deletions src/main/java/racingcar/View/InputView.java
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();
}
}
23 changes: 23 additions & 0 deletions src/main/java/racingcar/View/OutputView.java
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);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

중요한건 아니지만

Suggested change
System.out.printf("%s%s", WINNER_IS, winners);
System.out.printf(WINNER_IS + winners);

와 같이 작성하는 방법도 있습니다.

}
}
31 changes: 31 additions & 0 deletions src/main/java/racingcar/View/ViewConstants.java
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);
}
55 changes: 55 additions & 0 deletions src/main/java/racingcar/config/AppConfig.java
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

Choose a reason for hiding this comment

The 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

Choose a reason for hiding this comment

The 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);
}
}
57 changes: 57 additions & 0 deletions src/main/java/racingcar/controller/CarRacingController.java
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());
}
}
48 changes: 48 additions & 0 deletions src/main/java/racingcar/model/Car.java
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;
}
}
22 changes: 22 additions & 0 deletions src/main/java/racingcar/model/CarRacing.java
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();
}
}
Loading