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

[자동차 경주 게임] 유도영 미션 제출합니다. #528

Open
wants to merge 26 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
a9fc3ea
chore: 자바 버전 변경
yoondgu Dec 14, 2022
c5f7c13
style: 코드 포맷 통일
yoondgu Dec 14, 2022
9cc2fc5
docs: 미션 명세서 기능 목록 작성
yoondgu Dec 14, 2022
3324700
feat: 이름 형식을 쉼표 구분 <-> 리스트로 파싱하는 기능 구현
yoondgu Dec 14, 2022
921f02e
feat: 경주할 모든 자동차 이름을 형식에 맞게 입력받는 기능 구현
yoondgu Dec 14, 2022
a805b40
feat: 경주할 모든 자동차의 이름을 저장하는 기능 구현
yoondgu Dec 14, 2022
56ec0a5
feat: 랜덤 숫자를 생성하는 기능 구현
yoondgu Dec 14, 2022
f2d58e9
feat: 자동차가 전달받은 숫자에 따라 이동하는 기능 구현
yoondgu Dec 14, 2022
24e66d7
feat: 시도할 회수를 입력받는 기능 구현
yoondgu Dec 14, 2022
7c43478
fix: 자동차 이름 저장 기능에 중복 검증 로직 추가
yoondgu Dec 14, 2022
d081b01
test: 자동차 이름 저장 기능 테스트코드 작성
yoondgu Dec 14, 2022
a94e426
fix: 자동차 이동 오기능 수정, 테스트코드 작성
yoondgu Dec 14, 2022
d6604d6
feat: 전달받은 회수만큼 자동차 이동을 실행시키는 기능 구현
yoondgu Dec 14, 2022
bc10014
feat: 차수 별 실행 결과 출력하는 기능 구현
yoondgu Dec 14, 2022
6de02f1
feat: 자동차 간 위치를 비교해 우승자를 구하는 기능 구현
yoondgu Dec 14, 2022
cfff773
feat: 우승자 안내 출력하는 기능 구현
yoondgu Dec 14, 2022
f6de841
feat: 에러 메시지 출력하는 기능 구현
yoondgu Dec 14, 2022
1d001a5
feat: 잘못된 값 입력 시 그 부분부터 재입력하는 기능 구현
yoondgu Dec 14, 2022
13bce6f
refactor: 정적 상수, 메소드만 가진 클래스 객체 생성 방지
yoondgu Dec 14, 2022
70a9402
refactor: 입력 형식 검증 로직 파싱클래스로 분리
yoondgu Dec 14, 2022
d235762
refactor: 코드 스타일 요구사항 검토
yoondgu Dec 14, 2022
cd16886
refactor: racingCars 일급 콜렉션으로 분리
yoondgu Dec 14, 2022
8ba1b45
test: 자동차 이동 기능 예외사항 추가, 테스트 코드 추가 작성
yoondgu Dec 14, 2022
ed509c8
docs: 미션 명세서 클래스 구조 작성
yoondgu Dec 14, 2022
57d8c46
refactor: 자동차 위치 비교 getter 메소드 삭제, compareTo 사용하도록 로직 수정
yoondgu Dec 15, 2022
1c4f775
refactor: 개발 에러에 대한 예외처리 추가
yoondgu Dec 15, 2022
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
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ dependencies {

java {
toolchain {
languageVersion = JavaLanguageVersion.of(8)
languageVersion = JavaLanguageVersion.of(11)
}
}

Expand Down
143 changes: 143 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
# [미션 - 자동차 경주 게임] 명세서

## 목차

1. [기능 목록](#-기능-목록)
2. [상수값 목록](#-상수값-목록)
3. [단위 테스트 목록](#-단위테스트-목록)
4. [클래스 설계](#-클래스-설계)
5. [리팩토링 검토 목록](#%EF%B8%8F-리팩토링-검토-목록)

## 🚀 기능 목록

### 로직 분리에 따른 예외 발생 기준

- 예외 발생 시 적합한 표준예외를 사용한다.
- 기본 UI 검증 오류는 `view`에서, 도메인 기능에서 발생하는 오류는 `service`에서 예외를 발생시킨다.

### 도메인 구성 요소

- 자동차
- 이름
- 위치
- 게임에 참여한 모든 자동차 목록
- 자동차가 이동하기 위한 값 생성기
- 시도 회수
- 게임 결과
- 우승한 자동차 이름 모음 (1~n명)

### 도메인 로직을 위한 기능

1.
- [x] 경주할 모든 자동차의 이름을 저장하는 기능
- [x] `예외발생` : 경주할 자동차가 1대
- [x] `예외발생` : 이름의 길이가 5자 초과
- [x] `예외발생` : 중복 이름 존재
2.
- [x] 랜덤 숫자를 생성하는 기능
3.
- [x] 자동차가 전달받은 숫자에 따라 이동(전진하거나 전진하지않음)하는 기능
- [x] `예외발생` : 범위 밖 숫자
4.
- [x] 자동차 간 위치를 비교해 가장 값이 큰, 최소 1명 최대 n명의 우승자를 구하는 기능
5.
- [x] 전달받은 회수 만큼 자동차 이동을 실행시키는 기능

### UI 로직을 위한 기능

1.
- [x] 경주할 모든 자동차 이름을 형식에 맞게 입력받는 기능
- [x] `예외발생` : 빈 문자열
- [x] `예외발생` : 입력값 형식에 맞지 않음
2.
- [x] 시도할 회수를 입력받는 기능
- [x] `예외발생` : 자연수가 아닌 값
3.
- [x] 차수 별 실행 결과 출력하는 기능
4.
- [x] 우승자 안내 출력하는 기능
5.
- [x] 이름 형식을 쉼표 구분 <-> 리스트로 파싱하는 기능
6.
- [x] 에러 메시지 출력하는 기능
7.
- [x] 잘못된 값 입력 시 그 부분부터 재입력하는 기능

## 🗄 상수값 목록

### 도메인 로직 정보

- [x] 이동 숫자 0 ~ 9, 전진 조건 4
- [x] 이름 길이 상한 제한 5
- [x] 자동차 개수 하한 제한 2
- [x] 에러 메시지

### UI 로직 정보

- [x] 구분자 쉼표
- [x] 입력 안내 메시지
- [x] 실행 결과 출력 형식
- [x] 우승자 안내 문구 출력 형식
- [x] 에러 메시지
- [x] 에러메시지 출력 형식

## ✅ 단위테스트 목록

### 도메인 로직 단위테스트

- [x] 자동차 등록
- [x] 예외 테스트
- [x] 자동차 이동
- [x] 전진
- [x] 정지
- [x] 예외 테스트
- [x] 우승자 계산
- [x] 성공 테스트 (공동 우승 확인)

## 🖋 클래스 설계

```
└── racingcar
├── Application.java
├── controller
│ ├── RacingController.java
│ └── util
│ └── ExceptionHandler.java
├── dto
│ ├── CarDTO.java
│ └── CarStatusDTO.java
├── model
│ ├── NumberGenerator.java
│ ├── RacingGame.java
│ ├── RandomNumberGenerator.java
│ ├── constants
│ │ ├── ErrorMessage.java
│ │ └── GameRule.java
│ └── domain
│ ├── Car.java
│ └── RacingCars.java
└── view
├── InputView.java
├── OutputView.java
├── constants
│ ├── ErrorMessage.java
│ ├── Format.java
│ ├── InputMessage.java
│ └── OutputMessage.java
└── util
├── FormatParser.java
└── NumberParser.java
```

## ♻️ 리팩토링 검토 목록

### 코드 스타일 및 요구사항

- [x] 인덴트 2 이하 검토
- [x] 메소드, 클래스 분리 검토
- [x] 메소드 길이 10라인 이하 검토
- [x] 파라미터 개수 3개 이하 검토
- [x] 변수, 메소드 선언 순서 정리 (메소드 정렬 기준 1순위: 메소드 간 논리적 연관성, 2순위: public-private)
- [x] Java 코드 컨벤션 가이드 준수 검토
- [x] 요구사항에 주어진 클래스 적용 규칙 검토
- [x] TODO 주석 모두 해결 후 삭제
5 changes: 4 additions & 1 deletion src/main/java/racingcar/Application.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package racingcar;

import racingcar.controller.RacingController;

public class Application {
public static void main(String[] args) {
// TODO 구현 진행
RacingController racingController = new RacingController();
racingController.run();
}
}
12 changes: 0 additions & 12 deletions src/main/java/racingcar/Car.java

This file was deleted.

46 changes: 46 additions & 0 deletions src/main/java/racingcar/controller/RacingController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package racingcar.controller;

import java.util.List;
import racingcar.controller.util.ExceptionHandler;
import racingcar.dto.CarStatusDTO;
import racingcar.model.RacingGame;
import racingcar.model.RandomNumberGenerator;
import racingcar.view.InputView;
import racingcar.view.OutputView;

public class RacingController {
private final InputView inputView = new InputView();
private final OutputView outputView = new OutputView();
private final RacingGame racingGame = new RacingGame(new RandomNumberGenerator());

public void run() {
try {
repeatUntilGetLegalAnswer(this::enrollCarToRace);
repeatUntilGetLegalAnswer(this::moveCarsByCount);
showWinners();
} catch (Exception exception) {
exception.printStackTrace();
outputView.printErrorMessage(exception.getMessage());
}
}

private void repeatUntilGetLegalAnswer(Runnable runnable) {
ExceptionHandler.retryForIllegalArgument(runnable, outputView::printErrorMessage);
}

private void enrollCarToRace() {
List<String> carNames = inputView.inputCarNames();
racingGame.enrollCars(carNames);
}

private void moveCarsByCount() {
int moveCount = inputView.inputMoveCount();
List<CarStatusDTO> carStatuses = racingGame.repeatMovingCars(moveCount);
outputView.printGameResult(carStatuses);
}

private void showWinners() {
List<String> winnerNames = racingGame.findWinners();
outputView.printWinners(winnerNames);
}
}
19 changes: 19 additions & 0 deletions src/main/java/racingcar/controller/util/ExceptionHandler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package racingcar.controller.util;

import java.util.function.Consumer;

public class ExceptionHandler {
private ExceptionHandler() {
}

public static void retryForIllegalArgument(Runnable runnable, Consumer<String> exceptionMessageHandling) {
while (true) {
try {
runnable.run();
return;
} catch (IllegalArgumentException exception) {
exceptionMessageHandling.accept(exception.getMessage());
}
}
}
}
27 changes: 27 additions & 0 deletions src/main/java/racingcar/dto/CarDTO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package racingcar.dto;

public class CarDTO {
private final String name;
private final int position;

public CarDTO(String name, int position) {
this.name = name;
this.position = position;
}

public String getName() {
return name;
}

public int getPosition() {
return position;
}

@Override
public String toString() {
return "CarDTO{" +
"name='" + name + '\'' +
", position=" + position +
'}';
}
}
15 changes: 15 additions & 0 deletions src/main/java/racingcar/dto/CarStatusDTO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package racingcar.dto;

import java.util.List;

public class CarStatusDTO {
private final List<CarDTO> cars;

public CarStatusDTO(List<CarDTO> cars) {
this.cars = cars;
}

public List<CarDTO> getCars() {
return cars;
}
}
6 changes: 6 additions & 0 deletions src/main/java/racingcar/model/NumberGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package racingcar.model;

@FunctionalInterface
public interface NumberGenerator {
int make();
}
42 changes: 42 additions & 0 deletions src/main/java/racingcar/model/RacingGame.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package racingcar.model;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import racingcar.dto.CarDTO;
import racingcar.dto.CarStatusDTO;
import racingcar.model.domain.Car;
import racingcar.model.domain.RacingCars;

public class RacingGame {
private final RacingCars racingCars = new RacingCars();
private final NumberGenerator numberGenerator;

public RacingGame(NumberGenerator numberGenerator) {
this.numberGenerator = numberGenerator;
}

public void enrollCars(List<String> carNames) {
racingCars.addCars(carNames);
}

public List<CarStatusDTO> repeatMovingCars(int moveCount) {
List<CarStatusDTO> carStatuses = new ArrayList<>();
for (int count = 0; count < moveCount; count++) {
carStatuses.add(new CarStatusDTO(moveCars()));
}
return carStatuses;
}

private List<CarDTO> moveCars() {
racingCars.race(numberGenerator);
List<Car> cars = racingCars.cars();
return cars.stream()
.map(Car::to)
.collect(Collectors.toList());
}

public List<String> findWinners() {
return racingCars.findWinnerNames();
}
}
13 changes: 13 additions & 0 deletions src/main/java/racingcar/model/RandomNumberGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package racingcar.model;

import static racingcar.model.constants.GameRule.NUMBER_RANGE_END;
import static racingcar.model.constants.GameRule.NUMBER_RANGE_START;

import camp.nextstep.edu.missionutils.Randoms;

public class RandomNumberGenerator implements NumberGenerator {
@Override
public int make() {
return Randoms.pickNumberInRange(NUMBER_RANGE_START, NUMBER_RANGE_END);
}
}
11 changes: 11 additions & 0 deletions src/main/java/racingcar/model/constants/ErrorMessage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package racingcar.model.constants;

public class ErrorMessage {
public static final String CARS_LACK_OF_COUNT = "경주할 자동차는 최소 2대 필요합니다.";
public static final String CAR_NAME_LENGTH_OVER = "자동차 이름은 최대 5글자까지만 가능합니다.";
public static final String CARS_DUPLICATED_NAME = "중복된 자동차 이름을 등록할 수 없습니다.";
public static final String MOVING_NUMBER_OUT_OF_BOUNDS = "자동차 이동을 위한 값이 범위 밖 수입니다.";

private ErrorMessage() {
}
}
13 changes: 13 additions & 0 deletions src/main/java/racingcar/model/constants/GameRule.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package racingcar.model.constants;

public class GameRule {
public static final int NAME_LENGTH_UPPER_LIMIT = 5;
public static final int CARS_COUNT_LOWER_LIMIT = 2;
public static final int CAR_FORWARD_LOWER_LIMIT = 4;
public static final int NUMBER_RANGE_START = 0;
public static final int NUMBER_RANGE_END = 9;


private GameRule() {
}
}
Loading