Skip to content
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

Merged
merged 52 commits into from
Feb 18, 2020
Merged
Show file tree
Hide file tree
Changes from 39 commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
c993438
feat: 문자열 덧셈 계산기 첫번째 테스트 구현
include42 Feb 11, 2020
13da028
feat: 문자열 덧셈 계산기 두번째 테스트 구현
include42 Feb 11, 2020
9a2da16
feat: 문자열 덧셈 계산기 세번째 테스트 구현
include42 Feb 11, 2020
78fb041
feat: 문자열 덧셈 계산기 네번째 테스트 구현
include42 Feb 11, 2020
b1c8abc
feat: 문자열 덧셈 계산기 다섯번째 테스트 구현
include42 Feb 11, 2020
963e17a
feat: 문자열 덧셈 계산기 여섯번째 테스트 구현
include42 Feb 11, 2020
8477099
docs: README 파일 작성
include42 Feb 11, 2020
dc3016c
feat: 문자열 덧셈 계산기 일곱번째 테스트 구현
include42 Feb 11, 2020
dfe2f5d
feat: 입력 처리를 위한 InputView, Input 클래스
include42 Feb 11, 2020
ef0d1e2
refactor: StringAdder 리팩토링
include42 Feb 11, 2020
128f1a7
refactor: StringAdderTest 리팩토링
include42 Feb 11, 2020
de18aea
refactor: StringAdderTest,Input 리팩토링
include42 Feb 11, 2020
1e5178e
refactor: Input 내의 checkInputEmpty 수정
include42 Feb 11, 2020
ad00e5a
refactor: Input 클래스 리팩토링
include42 Feb 12, 2020
673f887
refactor: StringAdder 클래스 리팩토링
include42 Feb 12, 2020
58eb502
refactor: InputView 삭제 및 리팩토링
include42 Feb 12, 2020
ab8154e
refactor: Input 클래스 리팩토링
include42 Feb 12, 2020
2c80e2d
docs: README 기능목록 작성
include42 Feb 12, 2020
8f2c39e
feat: 레이싱 게임 첫번째 테스트
include42 Feb 12, 2020
64c1f48
feat: 레이싱 게임 두번째 테스트
include42 Feb 12, 2020
f133dd5
feat: 레이싱 게임 세번째 테스트
include42 Feb 12, 2020
1017249
refactor: RacingGame 코드 리팩토링
include42 Feb 12, 2020
e672def
feat: 레이싱 게임 네번째 테스트
include42 Feb 12, 2020
154d381
refactor: RacingGameTest 설계 변경 및 코드 리팩토링
include42 Feb 12, 2020
5728858
feat: Input, InputView 클래스 구현
include42 Feb 12, 2020
0d2db11
feat: 레이싱 게임 다섯번째 테스트
include42 Feb 12, 2020
16ca493
feat: 레이싱 게임 여섯번째 테스트
include42 Feb 12, 2020
062f133
feat: 레이싱 게임 일곱번째 테스트
include42 Feb 12, 2020
316334d
feat: 레이싱 게임 여덟번째 테스트
include42 Feb 12, 2020
6b35e41
feat: Output,OutputView 클래스 구현
include42 Feb 12, 2020
0141793
refactor: OutputView 외 나머지 클래스 리팩토링
include42 Feb 12, 2020
a615c2d
feat: Main 클래스 작성
include42 Feb 12, 2020
4fe2c6d
refactor: 패키지 생성 및 Input 설계 변경
include42 Feb 13, 2020
6d3910e
feat: Output 리팩토링
include42 Feb 13, 2020
a96232c
refactor: 코드 리팩토링
include42 Feb 13, 2020
0065e52
docs: README 구현단위 작성
include42 Feb 13, 2020
9c23b44
refactor: 코드 리팩토링
include42 Feb 13, 2020
866bc51
refactor: 클린코드 리팩터링
fucct Feb 13, 2020
96cad54
refactor: 오탈자 수정
fucct Feb 13, 2020
6ae1619
refactor: 축약어 제거
fucct Feb 14, 2020
83093f1
refactor: 에러메시지 상수화
fucct Feb 14, 2020
06b8a34
refactor: 클래스 이동
fucct Feb 14, 2020
de7cb6c
refactor: Random 시드 설정
fucct Feb 14, 2020
147a221
refactor: 메서드 분리
fucct Feb 15, 2020
9619d06
refactor: getDelimiter 메서드 리팩터링
fucct Feb 15, 2020
f7891c9
refactor: 일급컬렉션 생성
fucct Feb 16, 2020
722f72a
refactor: 오탈자 수정
fucct Feb 16, 2020
6d62621
refactor: 메서드 리팩터링
fucct Feb 17, 2020
7a8676f
refactor: 메서드 리팩터링
fucct Feb 17, 2020
9637bf0
refactor: 메서드 리팩터링
fucct Feb 17, 2020
5fe8b2e
refactor: 테스트 클래스, 메서드 추가
fucct Feb 17, 2020
8602a96
refactor: moveCars 메서드 리팩터링, 전략패턴 적용
fucct Feb 18, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 81 additions & 0 deletions README.md
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)
Empty file removed src/main/java/empty.txt
Empty file.
70 changes: 70 additions & 0 deletions src/main/java/racinggame/controller/Input.java
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 {
Copy link

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

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) {
Copy link

Choose a reason for hiding this comment

The 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) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Objects.isNull(list) 을 사용 해보면 어떨까요?
https://multifrontgarden.tistory.com/205

throw new IllegalArgumentException(LENGTH_ERROR_MESSAGE);
}
list.stream().forEach(Input::validateNameLength);
Copy link

Choose a reason for hiding this comment

The 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);
}
}
}
53 changes: 53 additions & 0 deletions src/main/java/racinggame/controller/Output.java
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);
}
}
51 changes: 51 additions & 0 deletions src/main/java/racinggame/domain/Car.java
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;
Copy link

Choose a reason for hiding this comment

The 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) {
Copy link

Choose a reason for hiding this comment

The 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;
}
}
13 changes: 13 additions & 0 deletions src/main/java/racinggame/domain/Main.java
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);
}
}

44 changes: 44 additions & 0 deletions src/main/java/racinggame/domain/RacingGame.java
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);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아래 글 참고해보세요!
https://withseungryu.tistory.com/1

}

public static void play(Input input, Output output) {
List<String> names = input.splitInputByComma();
List<Car> cars = new ArrayList<>();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아래 글을 읽고 일급컬렉션을 적용 해 보세요!
https://jojoldu.tistory.com/412

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();
}
}
}
21 changes: 21 additions & 0 deletions src/main/java/racinggame/view/InputView.java
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);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

축약어는 쓰지 않는게 좋을 것 같아요.
https://developerfarm.wordpress.com/2012/01/30/object_calisthenics_6/


/* 인스턴스를 사용하지 않으므로 추가 */
private InputView() {
}

public static String inputName() {
OutputView.printNameFormat();
return s.nextLine();
}

public static String inputRepeat() {
OutputView.printRepeatFormat();
return s.nextLine();
}
}
37 changes: 37 additions & 0 deletions src/main/java/racinggame/view/OutputView.java
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);
}
}
Loading