-
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
[디디] 레이싱게임 리뷰요청 드립니다. #86
Changes from 51 commits
c993438
13da028
9a2da16
78fb041
b1c8abc
963e17a
8477099
dc3016c
dfe2f5d
ef0d1e2
128f1a7
de18aea
1e5178e
ad00e5a
673f887
58eb502
ab8154e
2c80e2d
8f2c39e
64c1f48
f133dd5
1017249
e672def
154d381
5728858
0d2db11
16ca493
062f133
316334d
6b35e41
0141793
a615c2d
4fe2c6d
6d3910e
a96232c
0065e52
9c23b44
866bc51
96cad54
6ae1619
83093f1
06b8a34
de7cb6c
147a221
9619d06
f7891c9
722f72a
6d62621
7a8676f
9637bf0
5fe8b2e
8602a96
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,5 +1,86 @@ | ||
# java-racingcar | ||
자동차 경주 게임 미션 저장소 | ||
|
||
|
||
|
||
## TDD - 문자열 덧셈 계산기 | ||
|
||
### 요구사항 | ||
|
||
- 쉼표(,) 또는 콜론(:)을 구분자로 가지는 문자열을 입력받는다. | ||
- 또는, 커스텀 구분자를 가지는 문자열을 입력받는다. | ||
- 입력받은 문자열을 구분자를 통해 분리한다. | ||
- 분리한 값을 정수로 변환하여, 모두 더한다. | ||
- 더한 값을 출력한다. | ||
|
||
### 구현단위 | ||
|
||
- 덧셈 테스트 코드 구현 | ||
- 빈 값이나 null값이 들어오는 경우에 대한 테스트 코드 구현 | ||
- 숫자 1개만 들어오는 경우에 대한 테스트 코드 구현 | ||
- 숫자 여러개가 들어오는 경우에 대한 테스트 코드 구현 | ||
- 숫자가 음수인 경우에 대한 테스트 코드 구현 | ||
- 숫자 이외의 값이 들어오는 경우에 대한 테스트 코드 구현 | ||
- 커스텀 구분자를 사용했을 경우에 대한 테스트 코드 구현 | ||
|
||
### 예외처리 | ||
|
||
- RuntimeException : 숫자 이외의 값이 들어가거나, 음수가 전달될 경우 발생 | ||
|
||
|
||
|
||
## 자동차 경주 게임 | ||
|
||
### 요구사항 | ||
|
||
- 임의의 개수의 자동차 이름을 입력한다. | ||
- 각 자동차의 이름은 쉼표로 구분된다. | ||
- 각 자동차의 이름은 5글자 이하만 가능하다. | ||
- 이동할 횟수를 입력한다. | ||
- 이동할 횟수는 정수값만 입력이 가능하다. | ||
- 자동차는 랜덤으로 전진하거나 그 자리에 머무른다. | ||
- 0부터 9 사이의 난수를 생성하여, 그 값이 4 이상이면 전진한다. | ||
- 자동차 경주가 완료된 후, 누가 우승했는지 출력한다. | ||
- 우승자는 1명 이상이다. | ||
|
||
### 구현단위 | ||
|
||
**[TDD]** | ||
|
||
**Input** | ||
|
||
- 이름 1개 입력 시 가능여부 검증 | ||
- 빈 값이 들어오는 경우도 검증됨 | ||
- 이름 여러개 입력 시 분리 기능 검증 | ||
- 이름 여러개 입력 시 이름 길이 검증 | ||
- 횟수 입력 시 타입 에러 검증 | ||
- 횟수 입력 시 음수 입력 검증 | ||
|
||
**RacingGame** | ||
|
||
- 자동차의 이동 기능 테스트 | ||
- 랜덤값 생성 테스트 | ||
- 우승자 확인 테스트 | ||
- 레이싱게임 실행 결과 테스트 | ||
|
||
**Output** | ||
|
||
- 자동차 이동 시 출력되는 로그값 생성 테스트 | ||
|
||
|
||
|
||
**[UI 로직]** | ||
|
||
**View** | ||
|
||
- InputView 구현 | ||
- OutputView 구현 | ||
|
||
|
||
|
||
### 예외처리 | ||
|
||
- IllegalArgumentException : 이름의 길이가 1~5글자 사이가 아니거나, 횟수가 양수인 Integer값이 아닐 때 예외 발생 | ||
|
||
## 우아한테크코스 코드리뷰 | ||
* [온라인 코드 리뷰 과정](https://github.com/woowacourse/woowacourse-docs/blob/master/maincourse/README.md) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package racinggame.controller; | ||
|
||
import racinggame.domain.*; | ||
import racinggame.view.InputView; | ||
import racinggame.view.OutputView; | ||
|
||
public class Main { | ||
public static void main(String... args) { | ||
Names names = new Names(InputView.inputName()); | ||
Repeat repeat = new Repeat(InputView.inputRepeat()); | ||
Cars cars = new Cars(names); | ||
RacingGame racingGame = new RacingGame(); | ||
int maxPosition = 0; | ||
|
||
OutputView.printResultFormat(); | ||
for (int i = 0; i < repeat.getRepeat(); i++) { | ||
maxPosition = racingGame.moveCars(cars, maxPosition); | ||
OutputView.printCarsLog(cars); | ||
} | ||
Winners winners = cars.makeWinners(maxPosition); | ||
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. maxPosition값을 매번 전달 하기보단 경주가 끝난 자동차들을 기준으로 가장 큰 Position 값을 뽑고 해당 값으로 우승자를 판별 하는 것이 좋지 않을까요? |
||
OutputView.printWinnersLog(winners); | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
package racinggame.domain; | ||
|
||
public class Car { | ||
private static final int FORWARD_NUMBER = 4; | ||
private static final int START_POSITION = 0; | ||
private final String name; | ||
private int position; | ||
|
||
public Car(String name) { | ||
this(name, START_POSITION); | ||
} | ||
|
||
public Car(String name, int position) { | ||
this.name = name; | ||
this.position = position; | ||
} | ||
|
||
public boolean move(int random) { | ||
if (random >= FORWARD_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. 이 부분에 전략패턴을 적용 해 보세요! |
||
position++; | ||
return true; | ||
} | ||
return false; | ||
} | ||
|
||
public boolean isSamePosition(int position) { | ||
return this.position == position; | ||
} | ||
|
||
public String getName() { | ||
return name; | ||
} | ||
|
||
public int getPosition() { | ||
return position; | ||
} | ||
|
||
public int getMaxPosition(int maxPosition) { | ||
return Math.max(position, maxPosition); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
package racinggame.domain; | ||
|
||
import java.util.ArrayList; | ||
import java.util.Iterator; | ||
import java.util.List; | ||
|
||
public class Cars implements Iterable<Car> { | ||
private List<Car> cars; | ||
|
||
public Cars(Names names) { | ||
cars = names.makeCars(); | ||
} | ||
|
||
public Winners makeWinners(int maxPosition) { | ||
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. 외부로 부터 maxPosition을 받아오지말고 아래처럼 내부에 있는 Car list를 통해 maxPosition을 구해도 되지 않을까요?
|
||
List<Car> winners = new ArrayList<>(); | ||
|
||
for (Car car : cars) { | ||
if (car.isSamePosition(maxPosition)) { | ||
winners.add(car); | ||
} | ||
} | ||
return new Winners(winners); | ||
} | ||
|
||
public Iterator<Car> iterator() { | ||
return cars.iterator(); | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package racinggame.domain; | ||
|
||
import racinggame.domain.Car; | ||
import racinggame.domain.Cars; | ||
import racinggame.domain.Strategy; | ||
|
||
public class MoveCarsOnlyFalse implements Strategy { | ||
public int moveCars(Cars cars, int maxPosition) { | ||
for (Car car : cars) { | ||
if (car.move(0)) { | ||
maxPosition = car.getMaxPosition(maxPosition); | ||
} | ||
} | ||
return maxPosition; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package racinggame.domain; | ||
|
||
public class MoveCarsOnlyTrue implements Strategy { | ||
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. 전략패턴 적용 위치를 조금 더 고민 해보았으면 좋겠어요 🤔 현재 코드 상으로는 Strategy의 moveCars를 각각 구현 후 테스트 했기때문에 프러덕션 코드인 RacingGame에 대한 테스트가 되었다고 보기 힘들 것 같아요 ㅠㅠ |
||
public int moveCars(Cars cars, int maxPosition) { | ||
for (Car car : cars) { | ||
if (car.move(7)) { | ||
maxPosition = car.getMaxPosition(maxPosition); | ||
} | ||
} | ||
return maxPosition; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
package racinggame.domain; | ||
|
||
import racinggame.view.InputView; | ||
|
||
import java.util.ArrayList; | ||
import java.util.Arrays; | ||
import java.util.List; | ||
import java.util.Objects; | ||
|
||
public class Names { | ||
public static final String LENGTH_ERROR_MESSAGE = "이름의 길이는 1자 이상, 5자" + | ||
" 이하만 가능합니다."; | ||
private static final String SPLIT_DELIMITER = ","; | ||
private static final int NAME_LENGTH_LIMIT = 5; | ||
private List<String> names; | ||
|
||
public Names() { | ||
this(InputView.inputName()); | ||
} | ||
|
||
public Names(String inputNames) { | ||
names = splitInputByComma(inputNames); | ||
validateInput(); | ||
} | ||
|
||
public List<Car> makeCars() { | ||
List<Car> cars = new ArrayList<>(); | ||
for (String name : names) { | ||
cars.add(new Car(name)); | ||
} | ||
return cars; | ||
} | ||
|
||
public static List<String> splitInputByComma(String inputNames) { | ||
String[] splitName = inputNames.split(SPLIT_DELIMITER); | ||
return Arrays.asList(splitName); | ||
} | ||
|
||
public void validateInput() { | ||
if (Objects.isNull(names)) { | ||
throw new IllegalArgumentException(LENGTH_ERROR_MESSAGE); | ||
} | ||
for (String name : names) { | ||
validateNameLength(name); | ||
} | ||
} | ||
|
||
private static void validateNameLength(String name) { | ||
if (isNullName(name) || name.isEmpty() || isLongerThanFive(name)) { | ||
throw new IllegalArgumentException(LENGTH_ERROR_MESSAGE); | ||
} | ||
} | ||
|
||
private static boolean isNullName(String name) { | ||
return Objects.isNull(name); | ||
} | ||
|
||
private static boolean isLongerThanFive(String name) { | ||
return name.length() > NAME_LENGTH_LIMIT; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package racinggame.domain; | ||
|
||
import java.util.Random; | ||
|
||
public class RacingGame implements Strategy { | ||
public static final int NUMBER_BOUND = 10; | ||
|
||
public static int generateRandom() { | ||
Random rand = new Random(); | ||
rand.setSeed(System.nanoTime()); | ||
return rand.nextInt(NUMBER_BOUND); | ||
} | ||
|
||
public int moveCars(Cars cars, int maxPosition) { | ||
for (Car car : cars) { | ||
if (car.move(generateRandom())) { | ||
maxPosition = car.getMaxPosition(maxPosition); | ||
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. 음... 매번 움직일때 마다 MaxPosition을 선정 해 줄 필요가 있을까요? |
||
} | ||
} | ||
return maxPosition; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package racinggame.domain; | ||
|
||
import racinggame.view.InputView; | ||
|
||
public class Repeat { | ||
public static final String NUMBER_FORMAT_ERROR_MESSAGE = "횟수는 정수만 " + | ||
"가능합니다."; | ||
public static final String NOT_POSITIVE_ERROR_MESSAGE = "횟수는 1이상의" + | ||
" 정수만 가능합니다."; | ||
private final int repeat; | ||
|
||
public Repeat() { | ||
this(InputView.inputRepeat()); | ||
} | ||
|
||
public Repeat(int repeat) { | ||
this.repeat = repeat; | ||
} | ||
|
||
public Repeat(String repeat) { | ||
this(validateRepeat(repeat)); | ||
} | ||
|
||
private static int validateRepeat(String repeat) { | ||
try { | ||
int parseRepeat = Integer.parseInt(repeat); | ||
validateRepeatPositive(parseRepeat); | ||
return parseRepeat; | ||
} catch (NumberFormatException e) { | ||
throw new IllegalArgumentException(NUMBER_FORMAT_ERROR_MESSAGE); | ||
} | ||
} | ||
|
||
private static void validateRepeatPositive(int repeat) { | ||
if (repeat <= 0) { | ||
throw new IllegalArgumentException(NOT_POSITIVE_ERROR_MESSAGE); | ||
} | ||
} | ||
|
||
public int getRepeat() { | ||
return repeat; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package racinggame.domain; | ||
|
||
public interface Strategy { | ||
int moveCars(Cars cars, int maxPosition); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package racinggame.domain; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
public class Winners { | ||
List<String> winners; | ||
|
||
public Winners(List<Car> winnerCars) { | ||
winners = new ArrayList<>(); | ||
for (Car car : winnerCars) { | ||
winners.add(car.getName()); | ||
} | ||
} | ||
|
||
public List<String> getWinners() { | ||
return winners; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package racinggame.view; | ||
|
||
import java.util.Scanner; | ||
|
||
public class InputView { | ||
private static final Scanner scanner = new Scanner(System.in); | ||
|
||
/* 인스턴스를 사용하지 않으므로 추가 */ | ||
private InputView() { | ||
} | ||
|
||
public static String inputName() { | ||
OutputView.printNameFormat(); | ||
return scanner.nextLine(); | ||
} | ||
|
||
public static String inputRepeat() { | ||
OutputView.printRepeatFormat(); | ||
return scanner.nextLine(); | ||
} | ||
} |
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.
moveCars가 수행될때마다 cars를 넣어주는 이유가 있을까요?