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

[자동차 경주 게임] 베디 미션 제출합니다. #19

Merged
merged 37 commits into from
May 14, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
a707a5c
feat: Create Calculator Class
dpudpu May 8, 2019
77a2405
feat: Create TextCalculator class
dpudpu May 8, 2019
749bc6d
refactor: Create TextCalculatorMain class
dpudpu May 8, 2019
115436b
feat: Create Readme.md
dpudpu May 8, 2019
282850f
feat: 문자열을 입력받아 car 객체를 생성
dpudpu May 9, 2019
c7c1b37
feat: 반복회수를 입력받아 레이싱을 진행하고, 상태를 출력한다.
dpudpu May 9, 2019
6658f6e
feat: Create TestCode About RepeatNumber
dpudpu May 9, 2019
70f1e0d
feat: 우승자 출력 기능 구현
dpudpu May 9, 2019
47ee117
refactor: Extract RacingGame.run() method
dpudpu May 9, 2019
18bc46a
style: 코드 포맷팅
dpudpu May 9, 2019
d263662
refactor: RepeatNumber 클래스 repeatNumber 변수명 변경 -> number
dpudpu May 9, 2019
e8411fc
refactor: RacingGame.startRace() 메서드 추출
dpudpu May 9, 2019
eba48e3
refactor: ConsoleMessages enum 생성
dpudpu May 9, 2019
0ece395
refactor: Stream API 개선
dpudpu May 9, 2019
3f5fe50
refactor: 문자열 계산하기 if문 없이 구현.
dpudpu May 9, 2019
236c0d7
refactor: Rename Car.isMaxDistance() -> Car.isMatchDistance()
dpudpu May 10, 2019
7d906ab
refactor: carList 인스턴스 변수명을 cars로 변경
dpudpu May 10, 2019
7ed1147
refactor: Car.name 유효성 검사 버그 수정
dpudpu May 10, 2019
285d20f
feat: Car.name 테스트 코드 작성
dpudpu May 10, 2019
7b41ebf
feat: RacingCars 테스트 코드 작성
dpudpu May 10, 2019
4ba568a
feat: RepeatNumber 테스트 코드 작성
dpudpu May 10, 2019
b05c373
refactor: TextCalculator.calculatorMap을 CalculatorMapping.class로 추출
dpudpu May 12, 2019
598aff7
refactor TextCalculator.class의 모든 메소드 static 메소드로 변경
dpudpu May 12, 2019
3a1aaf3
style: Calculator와 Calculator구현클래스들 패키지 변경 cal -> cal.utils
dpudpu May 12, 2019
da85f16
refactor: Calculator enum으로 구현
dpudpu May 12, 2019
5d233f7
refactor: Operator enum을 없애고 Map.value에 직접 메소드 구현.
dpudpu May 12, 2019
37deb54
refactor: 잘 못된 변수명들 변경.
dpudpu May 12, 2019
9dd7cd8
refactor: InputViewTest 중복 사항 @After로
dpudpu May 13, 2019
93b20da
docs: README.md에 문자열 계산기 요구사항, 기능 추가
dpudpu May 13, 2019
54bb4ec
refactor: RaceStatusDto, RacingCars 생성자 생성시 필드 초기화 new ArrayList(cars)로
dpudpu May 13, 2019
a6fe5b2
refactor: InputView에서 String을 RepeatNumber로 변환해주는걸 RacingGame으로 변경
dpudpu May 13, 2019
2713a21
refactor: distance 초기화값 상수로 변경
dpudpu May 13, 2019
8bcb758
refactor: Car.getValidName -> validName, getTrimmedName -> trimName 변경
dpudpu May 13, 2019
01b2a21
refactor: Calculator 패키지 위치 재정의 cal.utils -> cal
dpudpu May 13, 2019
c5dc889
refactor: Car.getStatus() 제거 후 OutputView로 책임 전가
dpudpu May 13, 2019
dfede40
refactor: 코드 변경으로 인한 필요없어진 test들 삭제
dpudpu May 14, 2019
3d93975
refactor: TextCalculator 정적 메소드 제거, 생성자에 문자열을 인자 받는 형태로 변경
dpudpu May 14, 2019
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: 80 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,84 @@
# java-racingcar
자동차 경주 게임 미션 저장소


Copy link
Contributor

Choose a reason for hiding this comment

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

문자열 계산기에 대한 기능도 README.md 파일에 정리해 추가하면 좋을 것 같아요. 👍

Copy link
Author

Choose a reason for hiding this comment

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

미처 생각하지 못했네요. 추가했습니다.


## 기능 요구사항

- 주어진 횟수 동안 n대의 자동차는 전진 또는 멈출 수 있다.
- 각 자동차에 이름을 부여할 수 있다. 전진하는 자동차를 출력할 때 자동차 이름을 같이 출력한다.
- 자동차 이름은 쉼표(,)를 기준으로 구분하며 이름은 5자 이하만 가능하다.
- 사용자는 몇 번의 이동을 할 것인지를 입력할 수 있어야 한다.
- 전진하는 조건은 0에서 9 사이에서 random 값을 구한 후 random 값이 4 이상일 경우 전진하고, 3 이하의 값이면 멈춘다.
- 자동차 경주 게임을 완료한 후 누가 우승했는지를 알려준다. 우승자는 한 명 이상일 수 있다.



## 필요한 기능

1. 문자열을 입력받아 car 객체를 생성

- 문자열을 입력받는다.

- 문자열을 나눈다.

- car 객체들을 생성한다
- 일급컬렉션에 담아준다.

2. 반복회수를 입력받아 레이싱을 진행하고, 상태를 출력한다.
- 반복회수 숫자를 입력받는다.
- 반복회수마다 Car를 이동시킨다.
- Car는 랜덤 숫자를 생성하고, 그 숫자가 4 이상일 경우 한칸 전진한다.
- Car객체의 State를 출력한다.

3. 우승자를 구하고 출력한다.
- Car의 일급 컬렉션은 모든 Car에 대해 최대 이동 거리를 구한다.
- 최대 이동거리에 속하는 Car 객체를 구한다.
- Car객체의 Name을 출력한다.

## 가능한 예외

#### 1번케이스

- 쉼표로 구분되지 않는 경우.
- 구분된 문자열의 길이가 5를 초과하는 경우.
- Car 이름이 공백인 경우

#### 2번케이스

- 반복회수가 숫자가 아닌 경우.
- 반복회수가 0이거나 음수인 경우.





# 문자열 계산기



## 요구사항

- 문자열을 계산하는 계산기 (사칙연산 우선순위는 무시한다.)
- if문을 사용하지 않고 구현



## 기능

- 문자열을 입력 받으면 `Space`를 기준으로 분리해준다. ex) `3 + 2 * 4 / 10`
- 적절한 입력이 아닐 경우 예외처리 후 재입력한다.
- 분리한 연산자와 숫자들을 이용해서 계산해준다.
- 연산자를 Map의 key로 연산은 Value로 구현해준다.
- 연산자와 숫자를 넣으면 map에서 해당 연산자(value)의 연산메소드를 이용해서 연산 결과를 반환해준다.

-

## 해결과정

- https://dublin-java.tistory.com/38



## 우아한테크코스 코드리뷰
* [온라인 코드 리뷰 과정](https://github.com/woowacourse/woowacourse-docs/blob/master/maincourse/README.md)
* [온라인 코드 리뷰 과정](https://github.com/woowacourse/woowacourse-docs/blob/master/maincourse/README.md)
2 changes: 2 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ repositories {
dependencies {
testCompile('org.junit.jupiter:junit-jupiter:5.4.2')
testCompile('org.assertj:assertj-core:3.11.1')

testCompile('junit:junit:4.12')
}
21 changes: 21 additions & 0 deletions src/main/java/cal/Calculator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package cal;


import java.util.HashMap;
import java.util.Map;
import java.util.function.BiFunction;

final public class Calculator {
private static Map<String, BiFunction<Double, Double, Double>> operators = new HashMap<>();

static {
operators.put("+", (num1, num2) -> num1 + num2);
operators.put("-", (num1, num2) -> num1 - num2);
operators.put("*", (num1, num2) -> num1 * num2);
operators.put("/", (num1, num2) -> num1 / num2);
}

public static double calculate(String operator, double num1, double num2) {
return operators.get(operator).apply(num1, num2);
}
}
38 changes: 38 additions & 0 deletions src/main/java/cal/TextCalculator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package cal;

final public class TextCalculator {
Copy link
Contributor

Choose a reason for hiding this comment

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

Java의 제어자(modifier)는 아래의 순서로 표기하니 참고 부탁드려요.

Class and member modifiers, when present, appear in the order recommended by the Java Language Specification:

public protected private abstract default static final transient volatile synchronized native strictfp

private final static String DEFAULT_DELIMITER = " ";
private final String text;

public TextCalculator(final String text) {
this.text = text.trim();
}

public double calculate() {
String tokens[] = split(DEFAULT_DELIMITER);
double result = toDouble(tokens[0]);

for (int i = 1; i < tokens.length; i += 2) {
String operator = tokens[i];
double number = toDouble(tokens[i + 1]);
result = calculate(operator, result, number);
}
return result;
}

private String[] split(String delimiter) {
return text.split(delimiter);
}

private double calculate(String operator, double result, double number) {
return Calculator.calculate(operator, result, number);
}

private double toDouble(String value) {
try {
return Double.parseDouble(value);
} catch (Exception e) {
throw new IllegalArgumentException("적절한 입력이 아닙니다.");
}
}
}
11 changes: 11 additions & 0 deletions src/main/java/cal/TextCalculatorMain.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package cal;

import java.util.Scanner;

public class TextCalculatorMain {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
TextCalculator textCalculator = new TextCalculator(scanner.nextLine());
System.out.println(textCalculator.calculate());
}
}
Empty file removed src/main/java/empty.txt
Empty file.
8 changes: 8 additions & 0 deletions src/main/java/racing/Main.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package racing;

public class Main {
public static void main(String[] args) {
RacingGame racingGame = new RacingGame();
racingGame.run();
}
}
65 changes: 65 additions & 0 deletions src/main/java/racing/RacingGame.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package racing;

import racing.domain.Car;
import racing.domain.RacingCars;
import racing.domain.RepeatNumber;
import racing.view.ConsoleMessages;
import racing.view.InputView;
import racing.view.OutputView;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class RacingGame {
public void run() {
RacingCars racingCars = new RacingCars(generateCars());

startRace(racingCars, inputRepeatNumber());

OutputView.printWinners(racingCars.getWinners());
}

private List<Car> generateCars() {
try {
return getCarNames().stream()
.map(name -> new Car(name))
.collect(Collectors.toList());
} catch (Exception e) {
System.err.println(e.getMessage());
return generateCars();
}
}

private List<String> getCarNames() {
List<String> splitNames = Arrays.asList(InputView.inputCarNames().split(","));

if (splitNames.isEmpty()){
throw new IllegalArgumentException(ConsoleMessages.ERR_CAR_BLANK_NAME.getMessage());
}
return splitNames;
}

private static RepeatNumber inputRepeatNumber() {
return toRepeatNumber(InputView.inputRepeatNumber());
}


private static RepeatNumber toRepeatNumber(String number) {
try {
return new RepeatNumber(number);
} catch (NumberFormatException e) {
System.err.println(ConsoleMessages.ERR_REPEAT_NUMBER.getMessage());
} catch (IllegalArgumentException e) {
System.err.println(e.getMessage());
}
return inputRepeatNumber();
}

private void startRace(RacingCars racingCars, RepeatNumber repeatNumber) {
for (int i = 0; i < repeatNumber.getNumber(); i++) {
racingCars.race();
OutputView.printStatus(racingCars.getRaceStatus());
}
}
}
73 changes: 73 additions & 0 deletions src/main/java/racing/domain/Car.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package racing.domain;

import racing.view.ConsoleMessages;

public class Car implements Comparable<Car> {
Copy link
Contributor

Choose a reason for hiding this comment

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

Comparable 인터페이스 👍

private static final int DEFAULT_DISTANCE = 1;

private final String name;
private int distance;

public Car(final String name) {
this(name, DEFAULT_DISTANCE);
}

public Car(final String name, final int distance) {
this.name = validName(name);
this.distance = distance;
}

private String validName(String name) {
dpudpu marked this conversation as resolved.
Show resolved Hide resolved
String trimmedName = trimName(name);

checkBlankName(trimmedName);

checkNameLength(trimmedName);

return trimmedName;
}

private String trimName(String name) {
return name.trim();
}

private void checkBlankName(String trimmedName) {
if (trimmedName.isEmpty()) {
throw new IllegalArgumentException(ConsoleMessages.ERR_CAR_BLANK_NAME.getMessage());
}
}

private void checkNameLength(String trimmedName) {
if (trimmedName.length() > Rules.MAX_CAR_NAME) {
throw new IllegalArgumentException(ConsoleMessages.ERR_CAR_NAME.getMessage());
}
}

public int move(int number) {
if (isMove(number)) {
distance++;
}
return distance;
}

private boolean isMove(int number) {
return number >= Rules.MIN_MOVABLE_NUMBER;
}

public boolean isMatchDistance(Car car) {
return this.distance == car.distance;
}

public String getName() {
return name;
}

public int getDistance() {
return distance;
}

@Override
public int compareTo(Car car) {
return this.distance - car.distance;
}
}
16 changes: 16 additions & 0 deletions src/main/java/racing/domain/RaceStatusDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package racing.domain;

import java.util.ArrayList;
import java.util.List;

public class RaceStatusDto {
private final List<Car> cars;

public RaceStatusDto(List<Car> cars) {
this.cars = new ArrayList<>(cars);
}

public List<Car> getCars() {
return cars;
}
}
30 changes: 30 additions & 0 deletions src/main/java/racing/domain/RacingCars.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package racing.domain;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

public class RacingCars {
private final List<Car> cars;
Copy link
Contributor

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

Copy link
Author

Choose a reason for hiding this comment

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

좋은글 추천 감사합니다! 👍


public RacingCars(List<Car> cars) {
this.cars = new ArrayList<>(cars);
}

public void race() {
cars.forEach(car -> car.move(Rules.generateRandomNumber()));
}

public RaceStatusDto getRaceStatus() {
return new RaceStatusDto(cars);
Copy link
Contributor

Choose a reason for hiding this comment

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

RacingCarsList<Car> cars를 가지고 있으며, RaceStatusDto도 똑같이 List<Car> cars를 가지고 있습니다. RaceStatusDto는 왜 필요하며, 어떤 목적으로 만들어졌을까요? 그리고 이렇게 만들었을 때 어떤 이점이 있을까요?

Copy link
Author

@dpudpu dpudpu May 13, 2019

Choose a reason for hiding this comment

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

  1. RaceStatusDto는 왜 필요하며, 어떤 목적으로 만들어졌을까요?

    피드백해 주셨다시피 제가 주솟값을 그대로 복사하는 실수를 했지만, 목적은 RacingCars는 일급컬렉션이므로 cars를 getter로 직접 반환할 수가 없었습니다. 그렇다면 "cars의 상태 값을 어떻게 출력해야 좋을까?" 생각하다가 dto를 만들어주기로 하였고, 이렇게 해줌으로써 일급컬렉션의 조건 중 하나인 자료구조의 불변성을 지킬 수 있었습니다.

  2. 이렇게 만들었을 때 어떤 이점이 있을까요?

    • 외부에서 데이터를 수정하여도 직접적으로 db의 데이터를 건드는 것이 아니기 때문에 데이터의 안전성을 확보할 수 있습니다.

Copy link
Contributor

Choose a reason for hiding this comment

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

말씀하신 대로 RaceStatusDtoRacingCars를 외부로부터 지키기 위한 용도와 서로 다른 영역 간의 데이터를 이동하기 위한 목적입니다. 그럼 RaceStatusDto의 요구 사항은 애플리케이션 내부에서 나온 것일까요? 애플리케이션을 사용하는 외부에서 나온 것일까요?

}

public List<String> getWinners() {
Car winnerCar = Collections.max(cars);
return cars.stream()
.filter(car -> car.isMatchDistance(winnerCar))
.map(Car::getName)
.collect(Collectors.toList());
}
}
21 changes: 21 additions & 0 deletions src/main/java/racing/domain/RepeatNumber.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package racing.domain;

import racing.view.ConsoleMessages;

public class RepeatNumber {
Copy link
Contributor

Choose a reason for hiding this comment

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

RepeatNumber와 같은 클래스를 VALUE OBJECT라고 부른답니다. 💯 VALUE OBJECT는 불변이 특징입니다.

private int number;

public RepeatNumber(int number) {
if (number <= 0)
throw new IllegalArgumentException(ConsoleMessages.ERR_INVALID_REPEAT_NUMBER.getMessage());
this.number = number;
}

public RepeatNumber(String number) {
this(Integer.parseInt(number));
}

public int getNumber() {
return number;
}
}
Loading