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

[step3] 자동차 경주 리뷰 부탁드립니다 #1994

Merged
merged 12 commits into from
Mar 14, 2021
28 changes: 28 additions & 0 deletions src/main/java/new_racingcar/Car.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package new_racingcar;

public class Car {

private MoveStrategy moveStrategy;
private int distance;

public Car(MoveStrategy moveStrategy) {
this(moveStrategy, 0);
}

public Car(MoveStrategy moveStrategy, int distance) {
this.distance = distance;
this.moveStrategy = moveStrategy;
}

public Car move(int RandomValue) {
if (moveStrategy.isMove(RandomValue)) {
++distance;
}

return new Car(this.moveStrategy, distance);
}

public int getDistance() {
return distance;
}
}
40 changes: 40 additions & 0 deletions src/main/java/new_racingcar/Cars.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package new_racingcar;

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

public class Cars {
loop-study marked this conversation as resolved.
Show resolved Hide resolved
private final List<Car> cars;

public Cars(int playerCount) {
this.cars = createCar(playerCount);
}

public Cars(List<Car> cars) {
this.cars = cars;
}

private List<Car> createCar(int playerCount) {
List<Car> cars = new ArrayList<>();

for(int i = 0; i < playerCount; i++) {
cars.add(new Car(MoveOneStrategy.INSTANCE));

Choose a reason for hiding this comment

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

MoveOneStrategy를 GrandPrix에서 가지고 있는 것은 어떨까요?
자동차 이동 규칙은 게임 관리자가 가지고 있는게 맞다고 생각하는데 어떠신가요? 😄
그렇게 되면 Cars.run과 같은 함수도 테스트가 가능해질 것 같은데 말이죠.

}

return cars;
}

public Round run() {
List<Car> moveCars = cars.stream()
.map(c -> c.move(RandomUtil.getValue()))
loop-study marked this conversation as resolved.
Show resolved Hide resolved
.collect(Collectors.toList());

return new Round(new Cars(moveCars));
loop-study marked this conversation as resolved.
Show resolved Hide resolved
}

public List<Car> getUnmodifiableCars() {
return Collections.unmodifiableList(cars);
}
}
24 changes: 24 additions & 0 deletions src/main/java/new_racingcar/GrandPrix.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package new_racingcar;

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

public class GrandPrix {
private Cars cars;
private List<Round> rounds = new ArrayList<>();

private int roundCount;

public GrandPrix(int playerCount, int roundCount) {
this.cars = new Cars(playerCount);
this.roundCount = roundCount;
}

public List<Round> start() {
loop-study marked this conversation as resolved.
Show resolved Hide resolved
for(int i = 0; i < roundCount; i++) {
rounds.add(cars.run());
}

return rounds;
}
}
47 changes: 47 additions & 0 deletions src/main/java/new_racingcar/InputView.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package new_racingcar;

import java.util.Arrays;
import java.util.Scanner;

public class InputView {
private static final String INPUT_PLAYER_MESSAGE = "참가 선수를 입력해주세요";
private static final String INPUT_TURN_MESSAGE = "진행 횟수를 입력해주세요";

public int setPlayer() {
int playerConut = 0;
while (!isValid(playerConut)) {
playerConut = inputNumber(INPUT_PLAYER_MESSAGE);
}
return playerConut;
}

public boolean isValid(int playerCont) {
loop-study marked this conversation as resolved.
Show resolved Hide resolved
return (playerCont > 0);
}

public int inputNumber(String msg) {
int result = 0;

try {
Scanner scanner = new Scanner(System.in);
System.out.println(msg);
result = scanner.nextInt();
} catch (Exception e) {
printInputError();
}

return result;
}

public int setTurn() {
int turnCount = 0;
while (!isValid(turnCount)) {
turnCount = inputNumber(INPUT_TURN_MESSAGE);
}
return turnCount;
}

private void printInputError() {
System.out.println("잘못 입력하셨습니다");
}
}
14 changes: 14 additions & 0 deletions src/main/java/new_racingcar/MoveOneStrategy.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package new_racingcar;

public class MoveOneStrategy implements MoveStrategy {
private static final int MOVE_CONDITION = 4;

public static final MoveOneStrategy INSTANCE = new MoveOneStrategy();

private void MoveOneStrategy() { }

@Override
public boolean isMove(int randomValue) {
return MOVE_CONDITION <= randomValue;
}
}
6 changes: 6 additions & 0 deletions src/main/java/new_racingcar/MoveStrategy.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package new_racingcar;

public interface MoveStrategy {

boolean isMove(int randomValue);
loop-study marked this conversation as resolved.
Show resolved Hide resolved
}
50 changes: 50 additions & 0 deletions src/main/java/new_racingcar/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# [3단계] 레이싱카 정리

## 기능 요구사항
- 초간단 자동차 경주 게임을 구현한다.
- 주어진 횟수 동안 n대의 자동차는 전진 또는 멈출 수 있다.
- 사용자는 몇 대의 자동차로 몇 번의 이동을 할 것인지를 입력할 수 있어야 한다.
- 전진하는 조건은 0에서 9 사이에서 random 값을 구한 후 random 값이 4이상일 경우이다.
- 자동차의 상태를 화면에 출력한다. 어느 시점에 출력할 것인지에 대한 제약은 없다.

## 프로그래밍 요구사항
- 모든 로직에 단위 테스트를 구현한다. 단, UI(System.out, System.in) 로직은 제외
- 핵심 로직을 구현하는 코드와 UI를 담당하는 로직을 구분한다.
- UI 로직을 InputView, ResultView와 같은 클래스를 추가해 분리한다.
- 자바 코드 컨벤션을 지키면서 프로그래밍한다.
- 참고문서: https://google.github.io/styleguide/javaguide.html 또는 https://myeonguni.tistory.com/1596
- else 예약어를 쓰지 않는다.
- 힌트: if 조건절에서 값을 return하는 방식으로 구현하면 else를 사용하지 않아도 된다.
- else를 쓰지 말라고 하니 switch/case로 구현하는 경우가 있는데 switch/case도 허용하지 않는다.

## 기능 목록 및 commit 로그 요구사항
- 기능을 구현하기 전에 README.md 파일에 구현할 기능 목록을 정리해 추가한다.
- git의 commit 단위는 앞 단계에서 README.md 파일에 정리한 기능 목록 단위로 추가한다.
- 참고문서: AngularJS Commit Message Conventions

## 구현 기능 목록
- [X] 경주에 참가할 자동차 수를 입력 받는다
- [X] 1명 이 성공 확인
- [X] 0명 이하 실패 처리
- [X] 실행 횟수 n를 입력 받는다
- [X] 1번 이상 성공 확인
- [X] 0번 이하 실패 처리
- [X] 입력된 수 만큼 자동차 생성
- [X] 입력된 수 만큼 턴 진행
- [X] Random.nextInt(10) 이용해서 랜덤값 도출
- [X] 자동차는 값 4 이상만 이동
- [X] 자동차는 3 이하일때 움직임 없음
- [X] 라운드마다 결과저장
- [X] 레이스 종료 후 결과 출력












18 changes: 18 additions & 0 deletions src/main/java/new_racingcar/RacingMain.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package new_racingcar;

import java.util.List;

public class RacingMain {

public static void main(String[] args) {
InputView inputView = new InputView();
int playerCount = inputView.setPlayer();
int turnCount = inputView.setTurn();

GrandPrix grandPrix = new GrandPrix(playerCount, turnCount);
List<Round> rounds = grandPrix.start();

ResultView resultView = new ResultView();
resultView.printGrandPrixRecords(rounds);
loop-study marked this conversation as resolved.
Show resolved Hide resolved
}
}
13 changes: 13 additions & 0 deletions src/main/java/new_racingcar/RandomUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package new_racingcar;

import java.util.Random;

public class RandomUtil {
private static final int RANDOM_SIZE = 10;

private static Random random = new Random();

public static int getValue() {
return random.nextInt(RANDOM_SIZE);
}
}
31 changes: 31 additions & 0 deletions src/main/java/new_racingcar/ResultView.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package new_racingcar;

import java.util.List;

public class ResultView {

public void printGrandPrixRecords(List<Round> rounds) {
System.out.println("");
System.out.println("경기 결과");

rounds.stream()
.forEach(r -> printRound(r.getRoundInfo()));

System.out.println("경기가 종료되었습니다");
}

private void printRound(List<Car> cars) {
cars.stream()
.forEach(c -> printLab(c.getDistance()));

System.out.println("");
}

private void printLab(int distance) {
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < distance; i++) {
stringBuilder.append("-");
}
System.out.println(stringBuilder.toString());
}
}
15 changes: 15 additions & 0 deletions src/main/java/new_racingcar/Round.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package new_racingcar;

import java.util.List;

public class Round {
private final Cars cars;

public Round(Cars cars) {
this.cars = cars;
}

public List<Car> getRoundInfo() {
return cars.getUnmodifiableCars();
}
}
56 changes: 56 additions & 0 deletions src/test/java/new_racingcar/CarTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package new_racingcar;

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

import java.util.List;

import static org.junit.jupiter.api.Assertions.*;

@DisplayName("Car 테스트")
class CarTest {

// TODO : 자동차는 4 이상일 때 이동
@ParameterizedTest
@ValueSource(ints = {4,5,6,7})
@DisplayName("랜덤값 4 이상 자동차 이동거리 1 확인")
public void carMoveOneTtest(int randomValue) throws Exception {
//given
Car car = new Car(MoveOneStrategy.INSTANCE);
//when
car.move(randomValue);
//then
Assertions.assertThat(car.getDistance()).isEqualTo(1);
}


// TODO : 자동차는 3 이하일때 움직임 없음
@ParameterizedTest
@ValueSource(ints = {1,2,3})
@DisplayName("랜덤값 3 이하 자동차 이동거리 0 확인")
public void carMoveZeroTest(int randomValue) throws Exception {
//given
Car car = new Car(MoveOneStrategy.INSTANCE);
//when
car.move(randomValue);
//then
Assertions.assertThat(car.getDistance()).isEqualTo(0);
}

@Test
@DisplayName("읽기전용 cars 사용 시 에러 확인")
public void readOnlyCarTest() throws Exception {
//given
Cars cars = new Cars(5);
//when
List<Car> carList = cars.getUnmodifiableCars();

//then
assertThrows(Exception.class, () -> {
carList.add(new Car(MoveOneStrategy.INSTANCE));
});
}
}
27 changes: 27 additions & 0 deletions src/test/java/new_racingcar/CarsTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package new_racingcar;

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

import static org.assertj.core.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.*;

@DisplayName("Cars 테스트")
class CarsTest {

@ParameterizedTest
@ValueSource(ints = {3, 4, 5})
@DisplayName("자동차 생성 개수 확인")
public void createCarsSizeTest(int playerCount) throws Exception {
//given
Cars car = new Cars(playerCount);

//when
int carSize = car.getUnmodifiableCars().size();

//then
assertThat(carSize).isEqualTo(playerCount);
}
}