-
Notifications
You must be signed in to change notification settings - Fork 452
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
[자동차 경주 1단계] 카피 pr 요청 드립니다 #687
Changes from all commits
6a49f58
a441e90
0ab8f8d
492af6c
a180b01
c71801d
79f811c
8265649
d593791
a12bc57
fccb4fc
0725d76
0256e7c
f5681ac
a2bb927
93d4553
ff58cf0
d142726
e91b81a
f94ac96
537396b
954493f
3d6fa4b
7037759
69522c6
1cd6a33
beb56cb
e7703ab
39abf8b
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,41 @@ | ||||||||||||||
package racingcar; | ||||||||||||||
|
||||||||||||||
import racingcar.domain.Car; | ||||||||||||||
import racingcar.view.InputView; | ||||||||||||||
import racingcar.view.OutputView; | ||||||||||||||
|
||||||||||||||
import java.util.ArrayList; | ||||||||||||||
import java.util.List; | ||||||||||||||
import java.util.concurrent.ThreadLocalRandom; | ||||||||||||||
|
||||||||||||||
public class GameManager { | ||||||||||||||
|
||||||||||||||
private static final int MAX_RANDOM_NUMBER = 9; | ||||||||||||||
OutputView outputView = new OutputView(); | ||||||||||||||
InputView inputView = new InputView(); | ||||||||||||||
|
||||||||||||||
public void run() { | ||||||||||||||
outputView.printCarNameInputMessage(); | ||||||||||||||
List<String> carNames = inputView.getCarName(); | ||||||||||||||
List<Car> cars = makeNewCars(carNames); | ||||||||||||||
outputView.printTryCountInputMessage(); | ||||||||||||||
int tryCount = inputView.getTryCount(); | ||||||||||||||
for (int i = 0; i < tryCount; i++) { | ||||||||||||||
moveCars(cars); | ||||||||||||||
outputView.printTryResult(cars); | ||||||||||||||
} | ||||||||||||||
outputView.printWinners(cars); | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
private void moveCars(List<Car> cars) { | ||||||||||||||
for (Car car : cars) { | ||||||||||||||
car.moveCar(ThreadLocalRandom.current().nextInt(MAX_RANDOM_NUMBER + 1)); | ||||||||||||||
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. 해당 클래스는 Math.random()이나 Randdom 클래스와 어떤 차별점이 있고 뭘 위해 사용하셨나요? 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. Random은 seed를 설정하지 않으면 컴퓨터의 현재 시간을 seed로 사용합니다. ThreadLocalRandom은 Random처럼 하나의 인스턴스를 전역적으로 사용하는 것이 아닌, 스레드 별로 각각의 인스턴스를 할당하게 됩니다. 따라서 Random에서 발생할 수 있는 성능 저하를 해결 할 수 있다는 차별점이 있습니다. 현재 미션에서는 멀티 스레드 환경을 고려하지 않아도 무방하지만, ThreadLocalRandom 대신 Random을 사용할 이유가 없다고 생각하여 사용하였습니다! |
||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
private List<Car> makeNewCars(List<String> carNames) { | ||||||||||||||
return carNames.stream() | ||||||||||||||
.map(carName -> new Car(carName)) | ||||||||||||||
.toList(); | ||||||||||||||
Comment on lines
+37
to
+39
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
|
||||||||||||||
} | ||||||||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package racingcar; | ||
|
||
public class RacingcarApplication { | ||
public static void main(String[] args) { | ||
GameManager gameManager = new GameManager(); | ||
gameManager.run(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package racingcar.domain; | ||
|
||
public class Car { | ||
|
||
private static final int ZERO = 0; | ||
private static final int MINIMUM_MOVEMENT_CONDITION = 4; | ||
private final static int MAXIMUM_NAME_LENGTH = 5; | ||
private final static String MESSAGE_LENGTH_OF_CAR_NAME = "자동차 이름은 1자 이상 5자 이하여야 합니다."; | ||
private String name; | ||
private int distance; | ||
|
||
public Car(String name) { | ||
if(name.length() > MAXIMUM_NAME_LENGTH) { | ||
throw new IllegalArgumentException(MESSAGE_LENGTH_OF_CAR_NAME); | ||
} | ||
this.name = name; | ||
this.distance = 0; | ||
} | ||
|
||
public String getName() { | ||
return name; | ||
} | ||
|
||
public int getDistance() { | ||
return distance; | ||
} | ||
|
||
public void moveCar(int number) { | ||
if (distance < ZERO) { | ||
throw new IllegalArgumentException("이동 거리는 음수가 될 수 없습니다."); | ||
} | ||
Comment on lines
+29
to
+31
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. 메서드를 만들 때 많은 것을 검증하고자 하여서 불필요한 검증이 포함된 것 같습니다😅 |
||
if (number >= MINIMUM_MOVEMENT_CONDITION) { | ||
distance++; | ||
} | ||
Comment on lines
+32
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. 자동차가 전진하는 조건에 대한 규칙을 자동차가 알 필요는 없는 것 같습니다! 자동차는 전진하는 것에 책임만 가지고 메서드를 호출하는 부분에서 특정 숫자인지 검증하는 부분을 추가하였습니다! |
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
package racingcar.view; | ||
|
||
import java.util.List; | ||
import java.util.Scanner; | ||
|
||
public class InputView { | ||
|
||
private final static String MESSAGE_NO_DUPLICATE_CAR_NAMES = "중복된 자동차 이름은 사용할 수 없습니다."; | ||
private final static String MESSAGE_LENGTH_OF_CAR_NAME = "자동차 이름은 1자 이상 5자 이하여야 합니다."; | ||
private final static String MESSAGE_NO_SPACE = "중복된 자동차 이름은 사용할 수 없습니다."; | ||
private final static String MESSAGE_ONLY_NUMBER = "시도할 회수는 숫자여야만 가능합니다."; | ||
private final static String MESSAGE_ONLY_NATURAL_NUMBER = "시도할 회수는 자연수를 입력해 주세요."; | ||
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. 이 검증은 View에서만 해도 괜찮을까요? View는 코어 로직보다 쉽게 교체될 수 있고, 만약 다른 형태의 View로 교체된다면, 검증 로직이 날라가겠네요..🤔 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. View에서 직접 검증을 하지 않고 별도의 InputValidator 클래스를 생성하여 InputView의 검증을 하도록 변경하였습니다! |
||
private final static int MAXIMUM_NAME_LENGTH = 5; | ||
private final Scanner scanner = new Scanner(System.in); | ||
|
||
|
||
public List<String> getCarName() { | ||
String input = scanner.nextLine(); | ||
List<String> splitCarNames; | ||
try { | ||
validateNoSpace(input); | ||
splitCarNames = getSplitCarNames(input); | ||
validateLengthOfCarNames(splitCarNames); | ||
validateNoDuplicatedCarNames(splitCarNames); | ||
} catch (IllegalArgumentException e) { | ||
System.out.println(e.getMessage()); | ||
return getCarName(); | ||
} | ||
return splitCarNames; | ||
} | ||
|
||
public int getTryCount() { | ||
String input = scanner.nextLine(); | ||
int tryCount; | ||
try { | ||
tryCount = Integer.parseInt(input); | ||
validateNaturalNumber(tryCount); | ||
return tryCount; | ||
} catch (NumberFormatException e) { | ||
System.out.println(MESSAGE_ONLY_NUMBER); | ||
return getTryCount(); | ||
} catch (IllegalArgumentException e) { | ||
System.out.println(e.getMessage()); | ||
return getTryCount(); | ||
} | ||
} | ||
|
||
private void validateNaturalNumber(int tryCount) { | ||
if (tryCount <= 0) { | ||
throw new IllegalArgumentException(MESSAGE_ONLY_NATURAL_NUMBER); | ||
} | ||
} | ||
|
||
private void validateNoDuplicatedCarNames(List<String> splitCarNames) { | ||
long distinctCarCount = splitCarNames.stream() | ||
.distinct() | ||
.count(); | ||
if (distinctCarCount != splitCarNames.size()) { | ||
throw new IllegalArgumentException(MESSAGE_NO_DUPLICATE_CAR_NAMES); | ||
} | ||
} | ||
|
||
private void validateLengthOfCarNames(List<String> splitCarNames) { | ||
for (String carName : splitCarNames) { | ||
validateLengthOfCarName(carName); | ||
} | ||
} | ||
|
||
private void validateLengthOfCarName(String carName) { | ||
if (carName.isEmpty() || carName.length() > MAXIMUM_NAME_LENGTH) { | ||
throw new IllegalArgumentException(MESSAGE_LENGTH_OF_CAR_NAME); | ||
} | ||
} | ||
|
||
private List<String> getSplitCarNames(String input) { | ||
return List.of(input.split(",")); | ||
} | ||
|
||
private void validateNoSpace(String input) { | ||
if (input.contains(" ")) { | ||
throw new IllegalArgumentException(MESSAGE_NO_SPACE); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package racingcar.view; | ||
|
||
import racingcar.domain.Car; | ||
|
||
import java.util.List; | ||
|
||
public class OutputView { | ||
|
||
private static final String MESSAGE_NOT_EXIST_CAR = "생성된 자동차가 없습니다."; | ||
|
||
public void printTryResult(List<Car> cars) { | ||
System.out.println(); | ||
System.out.println("실행 결과"); | ||
for (Car car : cars) { | ||
System.out.println(car.getName() + " : " + "-".repeat(car.getDistance())); | ||
} | ||
} | ||
|
||
public void printWinners(List<Car> cars) { | ||
int longestDistance = cars.stream() | ||
.mapToInt(Car::getDistance) | ||
.max() | ||
.orElseThrow(() -> new IllegalArgumentException(MESSAGE_NOT_EXIST_CAR)); | ||
|
||
String winnerNames = String.join(", ", | ||
cars.stream() | ||
.filter(car -> car.getDistance() == longestDistance) | ||
.map(Car::getName) | ||
.toList() | ||
); | ||
|
||
System.out.println(winnerNames + "가 최종 우승했습니다."); | ||
} | ||
Comment on lines
+19
to
+33
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. 이 부분은 승자를 찾는 비즈니스 로직이 View에 존재하는 걸로 보이네요! 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 printCarNameInputMessage() { | ||
System.out.println("경주할 자동차 이름을 입력하세요(이름은 쉼표(,)를 기준으로 구분)."); | ||
} | ||
|
||
public void printTryCountInputMessage() { | ||
System.out.println("시도할 회수는 몇회인가요?"); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package racingcar.domain; | ||
|
||
import org.junit.jupiter.api.DisplayName; | ||
import org.junit.jupiter.api.Test; | ||
|
||
import static org.assertj.core.api.Assertions.*; | ||
|
||
class CarTest { | ||
|
||
private static final int MOVE_CONDITION = 5; | ||
private static final int STOP_CONDITION = 3; | ||
|
||
|
||
@Test | ||
@DisplayName("랜덤_숫자가_4_이상인_경우_전진") | ||
void moveCarTest() { | ||
Car car = new Car("capy"); | ||
int initDistance = car.getDistance(); | ||
car.moveCar(MOVE_CONDITION); | ||
int movedDistance = car.getDistance(); | ||
|
||
assertThat(movedDistance).isEqualTo(initDistance + 1); | ||
} | ||
|
||
@Test | ||
@DisplayName("랜덤_숫자가_3_이하인_경우_정지") | ||
void stopCarTest() { | ||
Car car = new Car("capy"); | ||
int initDistance = car.getDistance(); | ||
car.moveCar(STOP_CONDITION); | ||
int movedDistance = car.getDistance(); | ||
|
||
assertThat(movedDistance).isEqualTo(initDistance); | ||
} | ||
|
||
@Test | ||
@DisplayName("자동차_이름이_5자_이하인_경우_통과") | ||
void validCarName() { | ||
assertThatCode(() -> new Car("capy")) | ||
.doesNotThrowAnyException(); | ||
} | ||
|
||
@Test | ||
@DisplayName("자동차_이름이_5자_초과인_경우_에러_발생") | ||
void invalidCarName() { | ||
assertThatThrownBy(() -> new Car("capyeeee")) | ||
.isInstanceOf(IllegalArgumentException.class); | ||
} | ||
} |
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.
체크리스트 작성 👍🏿