-
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 39 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,70 @@ | ||
package racinggame.controller; | ||
|
||
import racinggame.view.InputView; | ||
|
||
import java.util.Arrays; | ||
import java.util.List; | ||
|
||
public class Input { | ||
public static final String LENGTH_ERROR_MESSAGE = "이름의 길이는 1자 이상, 5자" + | ||
" 이하만 가능합니다."; | ||
public static final String NUMBER_FORMAT_ERROR_MESSAGE = "횟수는 정수만 " + | ||
"가능합니다."; | ||
public static final String NOT_POSITIVE_ERROR_MESSAGE = "횟수는 1이상의" + | ||
" 정수만 가능합니다."; | ||
private static final String SPLIT_DELIMITER = ","; | ||
|
||
private String names; | ||
private String repeat; | ||
|
||
public Input() { | ||
this(InputView.inputName(), InputView.inputRepeat()); | ||
} | ||
|
||
public Input(String inputName, String inputRepeat) { | ||
this.names = inputName; | ||
validateInput(); | ||
|
||
this.repeat = inputRepeat; | ||
validateRepeat(); | ||
validateRepeatPositive(); | ||
} | ||
|
||
public List<String> splitInputByComma() { | ||
String[] splitName = names.split(SPLIT_DELIMITER); | ||
return Arrays.asList(splitName); | ||
} | ||
|
||
public int parseRepeatToInt() { | ||
return Integer.parseInt(repeat); | ||
} | ||
|
||
private static void validateNameLength(String name) { | ||
if (name == null || name.isEmpty() || name.length() > 5) { | ||
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 판별조건을 명시적으로 알 수 있게 메소드로 분리해 보면 어떨까요? |
||
throw new IllegalArgumentException(LENGTH_ERROR_MESSAGE); | ||
} | ||
} | ||
|
||
public void validateInput() { | ||
List<String> list = splitInputByComma(); | ||
if (list == null) { | ||
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.
|
||
throw new IllegalArgumentException(LENGTH_ERROR_MESSAGE); | ||
} | ||
list.stream().forEach(Input::validateNameLength); | ||
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 validateRepeat() { | ||
try { | ||
Integer.parseInt(repeat); | ||
} catch (NumberFormatException e) { | ||
throw new IllegalArgumentException(NUMBER_FORMAT_ERROR_MESSAGE); | ||
} | ||
} | ||
|
||
private void validateRepeatPositive() { | ||
int castingRepeat = parseRepeatToInt(); | ||
if (castingRepeat <= 0) { | ||
throw new IllegalArgumentException(NOT_POSITIVE_ERROR_MESSAGE); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
package racinggame.controller; | ||
|
||
import racinggame.domain.Car; | ||
import racinggame.view.OutputView; | ||
|
||
import java.util.*; | ||
|
||
public class Output { | ||
private static final String LOG_DELIMITER = " : "; | ||
private static final String POSITION_CHARACTER = "-"; | ||
private static final String SPLIT_DELIMITER = ","; | ||
|
||
private List<String> winners = new ArrayList<>(); | ||
private Map<String, Integer> carStatus; | ||
|
||
public void initCarStatus(List<String> names) { | ||
carStatus = new LinkedHashMap<>(); | ||
names.stream().forEach(name -> carStatus.put(name, 0)); | ||
} | ||
|
||
public String makeCarLog(String name, int position) { | ||
StringBuilder log = new StringBuilder(name); | ||
log.append(LOG_DELIMITER); | ||
for (int i = 0; i < position; i++) { | ||
log.append(POSITION_CHARACTER); | ||
} | ||
return log.toString(); | ||
} | ||
|
||
public void makeWinnerNames(List<Car> cars) { | ||
cars.stream().forEach(car -> car.addWinnerName(winners)); | ||
|
||
String winnerNames = String.join(SPLIT_DELIMITER, winners); | ||
OutputView.printWinners(winnerNames); | ||
} | ||
|
||
public void updateCarStatus(String name, int position) { | ||
carStatus.put(name, position); | ||
} | ||
|
||
public void printLog() { | ||
for (Map.Entry<String, Integer> entry : carStatus.entrySet()) { | ||
String name = entry.getKey(); | ||
String log = makeCarLog(name, carStatus.get(name)); | ||
OutputView.printLog(log); | ||
} | ||
OutputView.printLine(); | ||
} | ||
|
||
public boolean isContainName(String name) { | ||
return winners.contains(name); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
package racinggame.domain; | ||
|
||
import racinggame.controller.Output; | ||
|
||
import java.util.List; | ||
|
||
public class Car { | ||
private static final int FORWARD_NUMBER = 4; | ||
private static int maxPosition = 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. maxPosition을 static 필드로 모든 Car 인스턴스가 공유하게되면 어떤 문제가 있을까요? |
||
private static final int START_POSITION = 0; | ||
private 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 void 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++; | ||
maxPosition = Integer.max(position, maxPosition); | ||
} | ||
} | ||
|
||
public boolean isWinner() { | ||
return position == maxPosition; | ||
} | ||
|
||
public boolean match(int position) { | ||
return position == this.position; | ||
} | ||
|
||
public void addWinnerName(List<String> winners) { | ||
if (isWinner()) { | ||
winners.add(this.name); | ||
} | ||
} | ||
|
||
public void passingLog(Output output) { | ||
output.updateCarStatus(name, position); | ||
} | ||
|
||
public static void initMaxPosition() { | ||
maxPosition = 0; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package racinggame.domain; | ||
|
||
import racinggame.controller.Input; | ||
import racinggame.controller.Output; | ||
|
||
public class Main { | ||
public static void main(String... args) { | ||
Input input = new Input(); | ||
Output output = new Output(); | ||
RacingGame.play(input, output); | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package racinggame.domain; | ||
|
||
import racinggame.controller.Input; | ||
import racinggame.controller.Output; | ||
import racinggame.view.OutputView; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.Random; | ||
|
||
public class RacingGame { | ||
|
||
public static final int NUMBER_BOUND = 10; | ||
|
||
/* 인스턴스를 사용하지 않으므로 추가 */ | ||
private RacingGame() { | ||
} | ||
|
||
public static int generateRandom() { | ||
return new Random().nextInt(NUMBER_BOUND); | ||
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 void play(Input input, Output output) { | ||
List<String> names = input.splitInputByComma(); | ||
List<Car> cars = new ArrayList<>(); | ||
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. 아래 글을 읽고 일급컬렉션을 적용 해 보세요! |
||
int repeat = input.parseRepeatToInt(); | ||
|
||
output.initCarStatus(names); | ||
names.stream().forEach(x -> cars.add(new Car(x))); | ||
OutputView.printResultFormat(); | ||
moveCars(output, cars, repeat); | ||
output.makeWinnerNames(cars); | ||
} | ||
|
||
private static void moveCars(Output output, List<Car> cars, int repeat) { | ||
for (int i = 0; i < repeat; i++) { | ||
cars.stream().forEach(car -> { | ||
car.move(generateRandom()); | ||
car.passingLog(output); | ||
}); | ||
output.printLog(); | ||
} | ||
} | ||
} |
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 s = new Scanner(System.in); | ||
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 InputView() { | ||
} | ||
|
||
public static String inputName() { | ||
OutputView.printNameFormat(); | ||
return s.nextLine(); | ||
} | ||
|
||
public static String inputRepeat() { | ||
OutputView.printRepeatFormat(); | ||
return s.nextLine(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package racinggame.view; | ||
|
||
public class OutputView { | ||
public static final String NAME_FORMAT = "경주할 자동차 이름을 입력하세요(이름은 쉼표(,)를 기준으로 구분)"; | ||
public static final String REPEAT_FORMAT = "시도할 회수는 몇회인가요?"; | ||
public static final String RESULT_FORMAT = "\n실행 결과"; | ||
public static final String WINNER_FORMAT = "가 최종 우승했습니다."; | ||
|
||
/* 인스턴스를 사용하지 않으므로 추가 */ | ||
private OutputView() { | ||
|
||
} | ||
|
||
public static void printLog(String log) { | ||
System.out.println(log); | ||
} | ||
|
||
public static void printLine() { | ||
System.out.println(); | ||
} | ||
|
||
public static void printNameFormat() { | ||
System.out.println(NAME_FORMAT); | ||
} | ||
|
||
public static void printRepeatFormat() { | ||
System.out.println(REPEAT_FORMAT); | ||
} | ||
|
||
public static void printResultFormat() { | ||
System.out.println(RESULT_FORMAT); | ||
} | ||
|
||
public static void printWinners(String winnerNames) { | ||
System.out.println(winnerNames + WINNER_FORMAT); | ||
} | ||
} |
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.
아래 글을 읽고 mvc 패턴에 대해 좀더 고민 해 보는건 어떨까요?
https://apeltop.blogspot.com/2019/05/mvc-model-view-controller.html