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 51 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.
24 changes: 24 additions & 0 deletions src/main/java/racinggame/controller/Main.java
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);
Copy link

Choose a reason for hiding this comment

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

moveCars가 수행될때마다 cars를 넣어주는 이유가 있을까요?

OutputView.printCarsLog(cars);
}
Winners winners = cars.makeWinners(maxPosition);
Copy link

Choose a reason for hiding this comment

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

maxPosition값을 매번 전달 하기보단 경주가 끝난 자동차들을 기준으로 가장 큰 Position 값을 뽑고 해당 값으로 우승자를 판별 하는 것이 좋지 않을까요?

OutputView.printWinnersLog(winners);
}
}

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

Choose a reason for hiding this comment

The 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);
}
}
29 changes: 29 additions & 0 deletions src/main/java/racinggame/domain/Cars.java
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) {
Copy link

Choose a reason for hiding this comment

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

외부로 부터 maxPosition을 받아오지말고 아래처럼 내부에 있는 Car list를 통해 maxPosition을 구해도 되지 않을까요?

    public Winners makeWinners() {
        int maxPosition = getMaxPosition();

        List<Car> winners = cars.stream()
                .filter(car -> car.isSamePosition(maxPosition))
                .collect(Collectors.toList());
        return new Winners(winners);
    }

    private int getMaxPosition() {
        return cars.stream()
                    .mapToInt(Car::getPosition)
                    .max()
                    .orElse(0);
    }

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();
}

}
16 changes: 16 additions & 0 deletions src/main/java/racinggame/domain/MoveCarsOnlyFalse.java
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;
}
}
12 changes: 12 additions & 0 deletions src/main/java/racinggame/domain/MoveCarsOnlyTrue.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package racinggame.domain;

public class MoveCarsOnlyTrue implements Strategy {
Copy link

Choose a reason for hiding this comment

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

전략패턴 적용 위치를 조금 더 고민 해보았으면 좋겠어요 🤔
현재 자동차의 전진 조건은 전진하는 조건은 0에서 9 사이에서 random 값을 구한 후 random 값이 4 이상일 경우 전진하고, 3 이하의 값이면 멈춘다. 이고 전진 조건을 판별하는 로직은 Car클래스 안에 있어요. 따라서 피드백 드린 random값과 상관없이 자동차를 전진 시키려면 Car클래스 안에 있는 판별 로직에 대해 전략패턴을 적용하는 것이 맞을 것 같아요.

현재 코드 상으로는 Strategy의 moveCars를 각각 구현 후 테스트 했기때문에 프러덕션 코드인 RacingGame에 대한 테스트가 되었다고 보기 힘들 것 같아요 ㅠㅠ

public int moveCars(Cars cars, int maxPosition) {
for (Car car : cars) {
if (car.move(7)) {
maxPosition = car.getMaxPosition(maxPosition);
}
}
return maxPosition;
}
}
61 changes: 61 additions & 0 deletions src/main/java/racinggame/domain/Names.java
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;
}
}
22 changes: 22 additions & 0 deletions src/main/java/racinggame/domain/RacingGame.java
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);
Copy link

Choose a reason for hiding this comment

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

음... 매번 움직일때 마다 MaxPosition을 선정 해 줄 필요가 있을까요?

}
}
return maxPosition;
}
}
43 changes: 43 additions & 0 deletions src/main/java/racinggame/domain/Repeat.java
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;
}
}
5 changes: 5 additions & 0 deletions src/main/java/racinggame/domain/Strategy.java
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);
}
19 changes: 19 additions & 0 deletions src/main/java/racinggame/domain/Winners.java
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;
}
}
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 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();
}
}
Loading