-
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
[자동차 경주 게임] 박진훈 리뷰 부탁드립니다! #522
base: main
Are you sure you want to change the base?
Changes from all commits
0556a70
8f1220e
7935b9f
db4cb02
ca3eda7
c117b2b
134df50
3fc2bae
9f8abc4
76ad6d5
c3f9689
d001728
6cc73d5
5a0b432
e5e4263
1f07840
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,20 @@ | ||
# 자동차 경주 게임 | ||
|
||
## 기능 목록 | ||
|
||
### 자동차 게임 기능 | ||
- [x] 주어진 횟수를 입력받는 기능 | ||
- [x] 횟수가 숫자가 아닐시 예외 발생 기능 | ||
- [x] 횟수가 음수 또는 너무 큰 수 일시 예외 발생 기능 | ||
- [x] 주어진 횟수 동안 n대의 자동차는 전진 또는 멈춤 기능 | ||
- [x] 움직인 결과 출력 기능 | ||
- [x] 자동차 경주 게임을 완료한 후 누가 우승했는지 출력하는 기능 | ||
- [x] 우승자가 여러 명일 경우 쉼표(,)를 이용하여 구분하여 출력하는 기능 | ||
|
||
### 자동차 기능 | ||
- [x] 자동차에 이름을 입력받는 기능 | ||
- [x] 자동차 이름은 쉼표(,)를 기준으로 구분하는 기능 | ||
- [x] 쉼표(,) 여러개를 입력해도 인식 가능한 기능 | ||
- [x] 이름은 5자 초과시 예외 발생 기능 | ||
- [x] 자동차는 전진 또는 멈춤 기능 | ||
- [x] 0에서 9 사이에서 무작위 값을 구한 후 무작위 값이 4 이상일 때 전진 기능 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,11 @@ | ||
package racingcar; | ||
|
||
import racingcar.controller.RacingCarController; | ||
|
||
public class Application { | ||
|
||
public static void main(String[] args) { | ||
// TODO 구현 진행 | ||
RacingCarController racingCarController = new RacingCarController(); | ||
racingCarController.runGame(); | ||
} | ||
} |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package racingcar.controller; | ||
|
||
import racingcar.domain.CarRandomMoveNumberGenerator; | ||
import racingcar.domain.Cars; | ||
import racingcar.domain.RacingCarGame; | ||
import racingcar.domain.TryCommand; | ||
import racingcar.view.InputView; | ||
import racingcar.view.OutputView; | ||
|
||
public class RacingCarController { | ||
|
||
private final InputView inputView = new InputView(); | ||
private final OutputView outputView = new OutputView(); | ||
private RacingCarGame racingCarGame; | ||
|
||
public void runGame() { | ||
makeRacingCarGame(); | ||
playGame(); | ||
endGame(); | ||
} | ||
|
||
private void endGame() { | ||
outputView.printWinner(racingCarGame.findWinner()); | ||
} | ||
|
||
private void playGame() { | ||
TryCommand tryCommand = askTryCommand(); | ||
while(tryCommand.tryMove()){ | ||
racingCarGame.move(); | ||
outputView.printResult(racingCarGame.getCars()); | ||
} | ||
} | ||
|
||
private void makeRacingCarGame() { | ||
racingCarGame = new RacingCarGame(askCar()); | ||
outputView.printBlank(); | ||
} | ||
|
||
private TryCommand askTryCommand() { | ||
outputView.printInputTry(); | ||
return new ReEnterProcessor<>(inputView::readTryCommand, outputView::printExceptionMessage).process(); | ||
} | ||
|
||
|
||
private Cars askCar() { | ||
outputView.printInputCarName(); | ||
return new ReEnterProcessor<>(inputView::readCarName, outputView::printExceptionMessage).process(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package racingcar.controller; | ||
|
||
import java.util.function.Consumer; | ||
import java.util.function.Supplier; | ||
|
||
public class ReEnterProcessor<T> { | ||
private final Supplier<T> processor; | ||
private final Consumer<IllegalArgumentException> consumer; | ||
|
||
public ReEnterProcessor(Supplier<T> processor, Consumer<IllegalArgumentException> consumer) { | ||
this.processor = processor; | ||
this.consumer = consumer; | ||
} | ||
|
||
public T process() { | ||
while(true){ | ||
try { | ||
return processor.get(); | ||
}catch (IllegalArgumentException exception){ | ||
consumer.accept(exception); | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package racingcar.domain; | ||
|
||
import racingcar.exception.CarNameLengthException; | ||
|
||
public class Car { | ||
|
||
private static final int CAR_NAME_LENGTH = 5; | ||
private static final int MOVABLE_MIN_NUMBER = 4; | ||
private final String name; | ||
private int position = 0; | ||
|
||
public Car(String name) { | ||
validate(name); | ||
this.name = name; | ||
} | ||
|
||
private void validate(String name) { | ||
validateLength(name); | ||
} | ||
|
||
private void validateLength(String name) { | ||
if (name.length() > CAR_NAME_LENGTH) { | ||
throw new CarNameLengthException(); | ||
} | ||
} | ||
|
||
public void move(CarMoveNumberGenerator carMoveNumberGenerator) { | ||
final int number = carMoveNumberGenerator.generate(); | ||
|
||
if (number >= MOVABLE_MIN_NUMBER) { | ||
position++; | ||
} | ||
} | ||
|
||
public String getName() { | ||
return name; | ||
} | ||
|
||
public int getPosition() { | ||
return position; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package racingcar.domain; | ||
|
||
public interface CarMoveNumberGenerator { | ||
|
||
int generate(); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package racingcar.domain; | ||
|
||
import camp.nextstep.edu.missionutils.Randoms; | ||
|
||
public class CarRandomMoveNumberGenerator implements CarMoveNumberGenerator { | ||
|
||
private static final int RANDOM_LOWER_INCLUSIVE = 0; | ||
private static final int RANDOM_UPPER_INCLUSIVE = 9; | ||
|
||
@Override | ||
public int generate() { | ||
return Randoms.pickNumberInRange(RANDOM_LOWER_INCLUSIVE, RANDOM_UPPER_INCLUSIVE); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
package racingcar.domain; | ||
|
||
import racingcar.exception.CarsDuplicatedNameException; | ||
import racingcar.exception.CarsMaxScoreBlankException; | ||
|
||
import java.util.*; | ||
import java.util.stream.Collectors; | ||
|
||
public class Cars { | ||
|
||
private static final String DELIMITER = ","; | ||
private static final String DUPLICATED_DELIMITER_REGEX = ",+"; | ||
private final List<Car> cars; | ||
|
||
private Cars(List<Car> cars) { | ||
this.cars = Collections.unmodifiableList(cars); | ||
} | ||
Comment on lines
+15
to
+17
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 static Cars createCarNameByWord(String input) { | ||
List<Car> cars = new ArrayList<>(); | ||
String[] words = divideWord(input); | ||
validate(words); | ||
|
||
for (String carName : words) { | ||
cars.add(new Car(carName)); | ||
} | ||
return new Cars(cars); | ||
Comment on lines
+24
to
+27
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 static void validate(String[] words) { | ||
validateDuplicatedWord(words); | ||
} | ||
|
||
private static void validateDuplicatedWord(String[] words) { | ||
Set<String> uniqueWord = new HashSet<>(); | ||
for (String word : words) { | ||
if (!uniqueWord.add(word)) { | ||
throw new CarsDuplicatedNameException(); | ||
} | ||
} | ||
} | ||
|
||
private static String[] divideWord(String word) { | ||
return word.replaceAll(DUPLICATED_DELIMITER_REGEX, DELIMITER).split(DELIMITER); | ||
} | ||
|
||
public void move(CarMoveNumberGenerator carMoveNumberGenerator) { | ||
for (Car car : cars) { | ||
car.move(carMoveNumberGenerator); | ||
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 Winner findWinner() { | ||
int maxScore = findMaxScore(); | ||
return new Winner(cars.stream() | ||
.filter(car -> (car.getPosition() == maxScore)) | ||
.map(Car::getName) | ||
.collect(Collectors.toList())); | ||
} | ||
|
||
private int findMaxScore() { | ||
return cars.stream() | ||
.max(Comparator.comparingInt(Car::getPosition)) | ||
.map(Car::getPosition) | ||
.orElseThrow(CarsMaxScoreBlankException::new); | ||
} | ||
|
||
public List<Car> getCars() { | ||
return cars; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package racingcar.domain; | ||
|
||
public class RacingCarGame { | ||
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. 원래 |
||
|
||
private final Cars cars; | ||
|
||
public RacingCarGame(Cars cars) { | ||
this.cars = cars; | ||
} | ||
|
||
public void move() { | ||
cars.move(new CarRandomMoveNumberGenerator()); | ||
} | ||
|
||
public Winner findWinner() { | ||
return cars.findWinner(); | ||
} | ||
|
||
public Cars getCars() { | ||
return cars; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
package racingcar.domain; | ||
|
||
import racingcar.exception.TryCommandNumberException; | ||
import racingcar.exception.TryCommandRangeException; | ||
|
||
public class TryCommand { | ||
|
||
private static final int MIN_TRY = 1; | ||
private static final int MAX_TRY = 100000; | ||
private int tryCount; | ||
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. 불변으로 해도 값을 변경하는 방법이 있었군요.. |
||
|
||
private TryCommand(int tryCount) { | ||
this.tryCount = tryCount; | ||
} | ||
|
||
public static TryCommand createTryCommandByString(String input) { | ||
int number = convertInt(input); | ||
validate(number); | ||
return new TryCommand(number); | ||
} | ||
|
||
private static void validate(int number) { | ||
validateRange(number); | ||
} | ||
|
||
private static void validateRange(int number) { | ||
if(number < MIN_TRY || number > MAX_TRY) { | ||
throw new TryCommandRangeException(MIN_TRY, MAX_TRY); | ||
} | ||
} | ||
|
||
private static int convertInt(String input) { | ||
try{ | ||
return Integer.parseInt(input); | ||
}catch (NumberFormatException exception) { | ||
throw new TryCommandNumberException(); | ||
} | ||
} | ||
|
||
public boolean tryMove() { | ||
if(tryCount > 0) { | ||
tryCount--; | ||
return true; | ||
} | ||
return false; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package racingcar.domain; | ||
|
||
import java.util.List; | ||
|
||
public class Winner { | ||
|
||
private final List<String> winner; | ||
Comment on lines
+5
to
+7
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 Winner(List<String> winner) { | ||
this.winner = winner; | ||
} | ||
|
||
public List<String> getWinner() { | ||
return winner; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package racingcar.exception; | ||
|
||
public class CarNameLengthException extends IllegalArgumentException{ | ||
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. 커스텀 예외들은 장단점이 있습니다! 그래서 정답은 없지만 제가 사용한 이유는 |
||
|
||
private static final String EXCEPTION_MESSAGE_CAR_NAME_LENGTH = "[ERROR] 자동차이름은 5글자 이하입니다"; | ||
|
||
public CarNameLengthException() { | ||
super(EXCEPTION_MESSAGE_CAR_NAME_LENGTH); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package racingcar.exception; | ||
|
||
public class CarsDuplicatedNameException extends IllegalArgumentException { | ||
|
||
private static final String EXCEPTION_MESSAGE_CARS_DUPLICATED_NAME = "[ERROR] 자동차 이름은 중복될 수 없습니다"; | ||
|
||
public CarsDuplicatedNameException() { | ||
super(EXCEPTION_MESSAGE_CARS_DUPLICATED_NAME); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package racingcar.exception; | ||
|
||
public class CarsMaxScoreBlankException extends IllegalArgumentException{ | ||
|
||
private static final String EXCEPTION_MESSAGE_CARS_MAX_SCORE_BLANK = "[ERROR] 우승자를 찾을 수 없습니다."; | ||
|
||
public CarsMaxScoreBlankException() { | ||
super(EXCEPTION_MESSAGE_CARS_MAX_SCORE_BLANK); | ||
} | ||
} |
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.
컨벤션에 따라서 상수와 인스턴스 변수를 새 줄로 구분해주시는 게 좋을 것 같습니다!!
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.
감사합니다! 놓치고있던 부분이네요..