-
Notifications
You must be signed in to change notification settings - Fork 440
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
[연습] 자동차 경주 #514
base: main
Are you sure you want to change the base?
[연습] 자동차 경주 #514
Changes from 41 commits
3732b70
64f0da6
c74ffb9
d3b17d4
5402740
5c37b2f
a50d563
a38d016
06d72a5
61b8daf
16af14f
970064c
b9be6d0
d35210d
a0fd5c5
389d30f
15427cd
475188d
7f14435
780dc00
2eb4395
6854d10
391b802
a36a3f5
7548e0f
40e9197
0d8b21e
8eb4a81
9cb01d1
b7c46fa
9ae3034
694ec26
82bdc23
03a3227
4696e37
a61b1a5
dfd9714
b8fb51b
a0eda97
5e3d7d3
bc56861
2cff2fa
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 |
---|---|---|
@@ -0,0 +1,39 @@ | ||
# 🚗 구현 기능 목록 | ||
|
||
## 자동차 | ||
- [x] 각 자동차에 이름을 부여할 수 있다. | ||
- [x] **[예외처리]** 자동차의 이름이 5자 초과일 경우 예외처리한다. | ||
- [x] 전진하거나 멈출 수 있다. | ||
- [x] 연료가 4 이상일 경우 전진한다. | ||
- [x] **[예외처리]** 연료가 0 미만 9 초과의 값이 들어오면 예외처리한다. | ||
|
||
|
||
## 자동차 경주 게임 | ||
- [x] 경주에 참여할 자동차들을 등록한다. | ||
- [x] **[예외처리]** 참여할 자동차는 최소 1대여야한다. | ||
- [x] 이동 횟수를 등록한다. | ||
- [x] **[예외처리]** 이동횟수가 0 이하면 예외처리한다. | ||
- [x] 자동차 경주를 시작한다. | ||
- [x] 각 이동횟수마다 각 자동차에게 랜덤한 연료를 주입한다. | ||
- [x] 자동차 경주 게임을 완료한 후 누가 우승했는지를 알려준다.(여러명의 우승자가 나올 수 있다.) | ||
|
||
|
||
## 입력 | ||
- [x] 자동차 이름은 쉼표(,)를 기준으로 구분하며 이름을 입력받는다. | ||
- [x] **[예외처리]** 쉼표를 기준으로 나눴을 때 자동차 이름이 0보다 작거나 5보다 크면 예외처리한다. | ||
- [x] 사용자는 몇 번의 이동을 할 것인지를 입력할 수 있어야 한다. | ||
- [x] **[예외처리]** 이동 횟수는 숫자여야 한다. | ||
- [x] 사용자가 잘못된 값을 입력할 경우 에러 메시지를 출력 후 그 부분부터 입력을 다시 받는다. | ||
|
||
|
||
## 출력 | ||
- [x] 자동차 이름과 자동차의 현재 위치를 출력한다. | ||
- [x] 최종 우승자를 출력한다. | ||
- [x] 우승자가 여러 명일 경우 쉼표(,)를 이용하여 구분한다. | ||
|
||
--- | ||
## 동작 순서 | ||
1. 경주에 등록할 자동차를 입력받는다. | ||
2. 경주 시도 횟수를 입력받는다. | ||
3. 시도 횟수 만큼 자동차가 전진하거나 멈춘다. | ||
4. 경주가 종료되고 우승자를 알려준다. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,51 @@ | ||
package racingcar; | ||
|
||
public class Car { | ||
|
||
public static final int MIN_FUEL = 0; | ||
public static final int MAX_FUEL = 9; | ||
public static final int MIN_MOVE_FUEL = 4; | ||
|
||
public static final int MAX_NAME_LENGTH = 5; | ||
|
||
private final String name; | ||
private int position = 0; | ||
|
||
public Car(String name) { | ||
validateName(name); | ||
this.name = name; | ||
} | ||
|
||
// 추가 기능 구현 | ||
private void validateName(String name) { | ||
if (name.length() > MAX_NAME_LENGTH) { | ||
throw new IllegalArgumentException(Message.CAR_NAME_VALIDATION); | ||
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. 저는 예외 메세지는 추후에도 수정할 일이 없고, 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 void moveOrStop(int fuel) { | ||
validateFuel(fuel); | ||
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. Fuel 부분은 Randoms 라이브러리를 사용하는데, 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. 저도 같은 의견입니다 ! |
||
if (fuel >= MIN_MOVE_FUEL) { | ||
move(); | ||
} | ||
} | ||
Comment on lines
+29
to
+32
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. 도메인 함수 내부에서 다른 도메인 함수를 호출하는 것 보다 |
||
|
||
private void validateFuel(int fuel) { | ||
if (fuel < MIN_FUEL || fuel > MAX_FUEL) { | ||
throw new IllegalArgumentException(Message.CAR_FUEL_VALIDATION); | ||
} | ||
} | ||
|
||
private void move() { | ||
position++; | ||
} | ||
|
||
public int getCurrentPosition() { | ||
return position; | ||
} | ||
|
||
public String getName() { | ||
return name; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,70 @@ | ||||||||||||||
package racingcar; | ||||||||||||||
|
||||||||||||||
import camp.nextstep.edu.missionutils.Randoms; | ||||||||||||||
import java.util.ArrayList; | ||||||||||||||
import java.util.List; | ||||||||||||||
import java.util.stream.Collectors; | ||||||||||||||
|
||||||||||||||
public class CarGame { | ||||||||||||||
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. 'CarGame'이라고 네이밍을 하면 어떤 역할을 가지는 객체인지 추가적으로 CarGame이 너무 많은 역할을 하고 있는 것 같아요. |
||||||||||||||
|
||||||||||||||
private final List<Car> cars; | ||||||||||||||
private int moveCounts; | ||||||||||||||
|
||||||||||||||
public CarGame() { | ||||||||||||||
cars = new ArrayList<>(); | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
public void registerCar(List<String> cars) { | ||||||||||||||
validateCars(cars); | ||||||||||||||
for (String carName : cars) { | ||||||||||||||
this.cars.add(new Car(carName)); | ||||||||||||||
} | ||||||||||||||
Comment on lines
+19
to
+21
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.
Suggested change
이런식으로 map을 사용하는 코드는 어떠신가요? 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. 이 부분에 대해서는 stream으로 처리할 생각을 못했는데
Comment on lines
+13
to
+21
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. Car 객체들을 |
||||||||||||||
} | ||||||||||||||
|
||||||||||||||
private void validateCars(List<String> cars) { | ||||||||||||||
if (cars.isEmpty()) { | ||||||||||||||
throw new IllegalArgumentException(Message.NUMBER_OF_CARS_VALIDATION); | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
public List<Car> getCars() { | ||||||||||||||
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. Collection에 getter를 사용할 때 그냥 Collection 자체를 return하게 되면 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. 저도 같은 생각입니다 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. 감사합니다 이 부분은 처음 알았네요! |
||||||||||||||
return cars; | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
public void registerMoveCounts(int moveCounts) { | ||||||||||||||
validateMoveCounts(moveCounts); | ||||||||||||||
this.moveCounts = moveCounts; | ||||||||||||||
} | ||||||||||||||
Comment on lines
+34
to
+37
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. 도메인에서 한번 더 검증을 해줄 필요가 있을까? 라는 생각이 있었는데 |
||||||||||||||
|
||||||||||||||
private void validateMoveCounts(int moveCounts) { | ||||||||||||||
if (moveCounts <= 0) { | ||||||||||||||
throw new IllegalArgumentException(Message.MOVE_COUNTS_VALIDATION); | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
public void startRacing() { | ||||||||||||||
if (checkIsOver()) { | ||||||||||||||
throw new IllegalStateException(Message.GAME_OVER); | ||||||||||||||
} | ||||||||||||||
Comment on lines
+46
to
+48
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.
|
||||||||||||||
for (Car car : cars) { | ||||||||||||||
car.moveOrStop(getRandomFuel()); | ||||||||||||||
} | ||||||||||||||
moveCounts--; | ||||||||||||||
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 boolean checkIsOver() { | ||||||||||||||
return moveCounts <= 0; | ||||||||||||||
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. 0보다 작은 경우라고 표현하는 것보다 확실이 0일 경우 끝난다고 표현하는 것이 좋아보입니다 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. 확실히 우석님이 말씀하신대로 작성하는게 side effect 가 없을 거 같네요! |
||||||||||||||
} | ||||||||||||||
|
||||||||||||||
private int getRandomFuel() { | ||||||||||||||
return Randoms.pickNumberInRange(Car.MIN_FUEL, Car.MAX_FUEL); | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
public List<String> announceWinners() { | ||||||||||||||
return cars.stream() | ||||||||||||||
.filter(car -> car.getCurrentPosition() == cars.stream().map(Car::getCurrentPosition) | ||||||||||||||
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. 저도 Car에 Position을 얻기 위해 getter를 사용해서 받아왔었는데, 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. 좋은 자료 감사합니다!! |
||||||||||||||
.max(Integer::compareTo).orElse(0)) | ||||||||||||||
.map(Car::getName) | ||||||||||||||
.collect(Collectors.toList()); | ||||||||||||||
} | ||||||||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
package racingcar; | ||
|
||
import java.util.List; | ||
import racingcar.view.InputView; | ||
import racingcar.view.OutputView; | ||
|
||
public class MainController { | ||
|
||
private final CarGame carGame; | ||
private final InputView inputView; | ||
private final OutputView outputView; | ||
|
||
public MainController() { | ||
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. 클래스 변수로 받을 때 new로 생성 안하고, 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. 딱히 이유는 없었던거 같아요. |
||
carGame = new CarGame(); | ||
inputView = new InputView(); | ||
outputView = new OutputView(); | ||
} | ||
|
||
public void run() { | ||
registerCars(); | ||
registerMoveCounts(); | ||
startRace(); | ||
endRace(); | ||
} | ||
|
||
private void registerCars() { | ||
try { | ||
List<String> inputCars = inputView.inputCars(); | ||
carGame.registerCar(inputCars); | ||
} catch (IllegalArgumentException e) { | ||
outputView.printError(e.getMessage()); | ||
registerCars(); | ||
} | ||
} | ||
|
||
private void registerMoveCounts() { | ||
try { | ||
int moveCounts = inputView.inputMoveCounts(); | ||
carGame.registerMoveCounts(moveCounts); | ||
} catch (IllegalArgumentException e) { | ||
outputView.printError(e.getMessage()); | ||
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. catch에서 Error 메세지를 출력할 때, |
||
registerMoveCounts(); | ||
} | ||
} | ||
|
||
private void startRace() { | ||
outputView.printRaceStart(); | ||
while (!carGame.checkIsOver()) { | ||
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. "게임을 종료할 수 없는 동안" 이라는 조건보다, "게임이 진행되는 동안"으로 표현한다면 좀 더 직관적일 것 같습니다. |
||
carGame.startRacing(); | ||
outputView.printCars(carGame.getCars()); | ||
} | ||
} | ||
|
||
private void endRace() { | ||
outputView.printWinners(carGame.announceWinners()); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package racingcar; | ||
|
||
public class Message { | ||
|
||
public static final String CAR_NAME_VALIDATION = "자동차의 이름은 1~5자여야 합니다."; | ||
public static final String CAR_FUEL_VALIDATION = "연료는 0 이상 9 이하의 값이어야 합니다."; | ||
public static final String NUMBER_OF_CARS_VALIDATION = "등록할 자동차는 최소 1대여야 합니다."; | ||
public static final String MOVE_COUNTS_VALIDATION = "이동횟수는 최소 1회 이상이어야 합니다."; | ||
public static final String GAME_OVER = "경기가 종료되었습니다."; | ||
|
||
public static final String INPUT_CAR_MESSAGE = "경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)"; | ||
public static final String INPUT_MOVE_COUNTS_MESSAGE = "시도할 회수는 몇회인가요?"; | ||
public static final String INPUT_NUMBER_ERROR = "숫자만 입력이 가능합니다."; | ||
|
||
public static final String OUTPUT_RACE_RESULT = "\n실행 결과"; | ||
|
||
} |
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,49 @@ | ||||||||
package racingcar.view; | ||||||||
|
||||||||
import camp.nextstep.edu.missionutils.Console; | ||||||||
import java.util.Arrays; | ||||||||
import java.util.List; | ||||||||
import java.util.regex.Pattern; | ||||||||
import java.util.stream.Collectors; | ||||||||
import racingcar.Car; | ||||||||
import racingcar.Message; | ||||||||
|
||||||||
public class InputView { | ||||||||
|
||||||||
public static final String CAR_REGEX = ","; | ||||||||
private static final String ONLY_NUMBER_REGEX = "^[0-9]+$"; | ||||||||
|
||||||||
public List<String> inputCars() { | ||||||||
System.out.println(Message.INPUT_CAR_MESSAGE); | ||||||||
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. 입력 문구를 출력하는 것도 출력하는 책임이라고 생각해서 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. 흠..저도 프리코스 진행할 때 고민했었는데 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. InputView 클래스 자체가 입력에 관한 책임을 가지기 때문에 저도 입력에 필요한 출력은 InputView에서 담당하는것이 좀 더 낫다고 생각합니다 ! |
||||||||
String input = Console.readLine(); | ||||||||
validateInputCars(input); | ||||||||
String[] inputCars = input.split(CAR_REGEX); | ||||||||
return Arrays.stream(inputCars).collect(Collectors.toList()); | ||||||||
Comment on lines
+20
to
+21
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.
Suggested change
한번 밖에 쓰이지 않는 변수를 할당하는 것보다 합치는 것이 어떨까요? 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. 놓친거 같아요! 확실히 깔끔해보이네요~ |
||||||||
} | ||||||||
|
||||||||
private void validateInputCars(String input) { | ||||||||
if (input.endsWith(CAR_REGEX)) { | ||||||||
throw new IllegalArgumentException(Message.CAR_NAME_VALIDATION); | ||||||||
} | ||||||||
|
||||||||
String[] inputCars = input.split(CAR_REGEX); | ||||||||
for (String inputCar : inputCars) { | ||||||||
if (inputCar.length() > Car.MAX_NAME_LENGTH) { | ||||||||
throw new IllegalArgumentException(Message.CAR_NAME_VALIDATION); | ||||||||
} | ||||||||
} | ||||||||
Comment on lines
+30
to
+34
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. 자동차의 이름에 대한 유효성 검증은 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 int inputMoveCounts() { | ||||||||
System.out.println(Message.INPUT_MOVE_COUNTS_MESSAGE); | ||||||||
String input = Console.readLine(); | ||||||||
validateNumber(input); | ||||||||
return Integer.parseInt(input); | ||||||||
} | ||||||||
|
||||||||
private void validateNumber(String input) { | ||||||||
if (!Pattern.matches(ONLY_NUMBER_REGEX, input)) { | ||||||||
throw new IllegalArgumentException(Message.INPUT_NUMBER_ERROR); | ||||||||
} | ||||||||
} | ||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
package racingcar.view; | ||
|
||
import java.util.List; | ||
import racingcar.Car; | ||
import racingcar.Message; | ||
|
||
public class OutputView { | ||
|
||
public static final String CAR_FORMAT = "%s : %s\n"; | ||
public static final String MOVE_UNIT = "-"; | ||
public static final String DELIMITER = ", "; | ||
public static final String ERROR_SUFFIX = "[ERROR]"; | ||
public static final String WINNER_FORMAT = "최종 우승자 : %s"; | ||
public static final String ERROR_FORMAT = "%s %s\n"; | ||
|
||
public void printCars(List<Car> cars) { | ||
for (Car car : cars) { | ||
printCar(car); | ||
} | ||
printNewLine(); | ||
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. 이런식으로 메서드를 만드는 것이 좋아보이네요 배워갑니다! |
||
} | ||
|
||
private void printNewLine() { | ||
System.out.println(); | ||
} | ||
|
||
public void printRaceStart() { | ||
System.out.println(Message.OUTPUT_RACE_RESULT); | ||
} | ||
|
||
private void printCar(Car car) { | ||
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. 포맷으로 출력하는 System.put.printf를 몰랐었는데 |
||
System.out.printf(CAR_FORMAT, car.getName(), getMove(car.getCurrentPosition())); | ||
} | ||
|
||
private String getMove(int currentPosition) { | ||
StringBuilder stringBuilder = new StringBuilder(); | ||
for (int i = 0; i < currentPosition; i++) { | ||
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. 반복문 변수도 i 말고 네이밍 해주는 것이 좋을 것 같습니다! |
||
stringBuilder.append(MOVE_UNIT); | ||
} | ||
return stringBuilder.toString(); | ||
} | ||
|
||
public void printWinners(List<String> winners) { | ||
String winnersName = String.join(DELIMITER, winners); | ||
System.out.printf(WINNER_FORMAT, winnersName); | ||
} | ||
|
||
public void printError(String errorMessage) { | ||
System.out.printf(ERROR_FORMAT, ERROR_SUFFIX, errorMessage); | ||
} | ||
} |
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.
기능 목록을 자세히 작성하신 것이 보기 좋은 것 같습니다 !