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

[자동차 경주 게임] 최현구 미션 제출합니다. #269

Closed
wants to merge 44 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
f37540c
feat(README.md): update README.md
Hyeon9mak Dec 2, 2020
2221156
fix(README.md): fix '~' to '-'
Hyeon9mak Dec 2, 2020
141ea97
feat(README.md): add attention list
Hyeon9mak Dec 2, 2020
e0a87f8
feat(README.md): add implemention list
Hyeon9mak Dec 4, 2020
a7855cf
style(README.md): fix enterline
Hyeon9mak Dec 4, 2020
0bc4442
test(Car): add create Car object
Hyeon9mak Dec 4, 2020
e9957f6
feat(Car): add method about position
Hyeon9mak Dec 4, 2020
b60dc3a
test(Car): test Car methods
Hyeon9mak Dec 4, 2020
57e3855
test(InputUtils): add user input test
Hyeon9mak Dec 4, 2020
89d013e
feat(InputUtils): add user input exception
Hyeon9mak Dec 4, 2020
d77d980
test(InputUtils): add user input exception test
Hyeon9mak Dec 4, 2020
f489643
test(Track): add track object test
Hyeon9mak Dec 4, 2020
21531bb
test(Track): modify Track constructor parameter
Hyeon9mak Dec 4, 2020
f3d1b75
feat(Track): add Track object
Hyeon9mak Dec 4, 2020
0222dec
feat(README.md): add attention list
Hyeon9mak Dec 4, 2020
0721c65
test(Gas): add Gas object test code
Hyeon9mak Dec 4, 2020
e986952
feat(Gas): add Gas object
Hyeon9mak Dec 4, 2020
ac20a93
feat(README.md): add implemention list
Hyeon9mak Dec 4, 2020
5cdcbfc
style(GasTank): rename class
Hyeon9mak Dec 4, 2020
c9f67c7
refactor(GasTank): fix method return type
Hyeon9mak Dec 4, 2020
3229e2b
feat(Car): add Car move method
Hyeon9mak Dec 4, 2020
c01ce4b
feat(README.md): add exception list
Hyeon9mak Dec 4, 2020
a5da9a3
test: refactoring test codes
Hyeon9mak Dec 4, 2020
022c18e
feat(I/O): add I/O utils
Hyeon9mak Dec 4, 2020
3d994d1
test: fix TrackTest and InputUtilsTest
Hyeon9mak Dec 5, 2020
373047e
test: add Car object constructor test
Hyeon9mak Dec 5, 2020
8c97664
refactor(Car): fix constructor parameter
Hyeon9mak Dec 5, 2020
6632da0
feat: add racing winner award
Hyeon9mak Dec 5, 2020
31330e9
feat(Car): add getName method
Hyeon9mak Dec 5, 2020
5cef028
feat(README.md): add implemention list
Hyeon9mak Dec 5, 2020
70e8f17
feat: add print during play racing game
Hyeon9mak Dec 5, 2020
9beeb8d
style(README.md): fix emoge
Hyeon9mak Dec 5, 2020
8d86cf2
style: fix whitespace
Hyeon9mak Dec 5, 2020
460ac9a
feat(README.md): add attention list
Hyeon9mak Dec 5, 2020
4055027
refactor(GasTank): fix constructor -> static factory method
Hyeon9mak Dec 6, 2020
003766d
refactor(Car): fix constructor -> static factory method
Hyeon9mak Dec 6, 2020
015baf0
refactor(Track): fix constructor -> static factory method
Hyeon9mak Dec 6, 2020
f5b3b8a
feat(README.md): add attention list
Hyeon9mak Dec 6, 2020
6198ebc
refactor: add final keyword in member variable
Hyeon9mak Dec 7, 2020
d98b36d
feat(README.md): add exception list
Hyeon9mak Dec 7, 2020
7642b08
refactor(Track->Racing): rename class
Hyeon9mak Dec 7, 2020
b7e131b
feat(Cars): add Cars (Car class list)
Hyeon9mak Dec 7, 2020
9923b12
refactor: refactoring project
Hyeon9mak Dec 7, 2020
4cc37bb
refactor: split Object list and implement list
Hyeon9mak Dec 7, 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
95 changes: 95 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,99 @@
# 미션 - 자동차 경주 게임
## 🔍 객체 목록
- Car 객체
- Car가 아는 것
- 자신의 이름
- 자신의 위치
- Car가 하는 것
- position 이동
- 자신의 position 상태 반환
- 자신이 우승자인지 판단
- 유저 입력에 따른 Car 이름 설정
- 유저 입력 예외처리
- Cars 객체 (일급 컬렉션?)
- Cars가 아는 것
- Car 리스트
- Cars가 하는 것
- Car 들에게 각자의 position 반환 요청
- Car 들에게 각자 우승자인지 판별요청
- GasTank 객체
- GasTank가 아는 것
- 정지, 출발 기준
- GasTank가 하는 것
- 정지, 출발 결정
- Lap 객체
- Lap이 아는 것
- 최종 lap
- 현재 lap
- Lap이 하는 것
- 최종 lap과 현재 lap 비교
- ~~Track 객체~~ Racing 컨트롤러
- Racing이 아는 것
- (없음)
- Racing이 하는 것
- Lap만큼 Racing 루프 반복하기
- 도메인들의 메서드 결과 View에 전달하기

<br>

## 🎯 구현할 기능 목록
- 멈춤(0-3), 전진(4-9) 구현 ✔️
- 0~9 랜덤 값 클래스 Gas 구현 ✔️
- 유저 입력에 따른 멈춤, 전진 기능 반복 ✔️
- 유저 입력 예외처리 ✔️
- 최종 우승자 출력 구현 ✔️
- 시도 회수별 게임 진행 상황(실행 결과) 출력 ✔

<br>

## 🤔 예외처리 목록
- 자동차 이름️ ✔️
- 5자리 초과️ ✔️
- 빈 문자열 ✔️
- ',' 이외 빈 문자열 혹은 공백(space) ✔️
- 중복 이름 ✔️
- Track 루프 횟수 ✔️
- 숫자가 아닌 경우 ✔️
- 빈 문자열 ✔️
- 음수인 경우 ✔️


<br>

## 😅 ️꾸준히 확인할 목록
- README.md 가 죽지 않도록 꾸준히 업데이트
- **접근제한 꼼꼼하게 확인하기**
- 인스턴스/클래스 변수는 최대한 숨기자
- **static 남발하지 않기**
- 객체의 필드(인스턴스 변수, 멤버)가 없는 경우에만 클래스(static) 메소드 생성
- 객체의 전체 인스턴스 수를 파악하기 위한 클래스(static) 변수 사용
- (인스턴스 추적을 위해서만 상위 class를 개설하는 것은 낭비)
- **확장성 계속 고려하기**
- Car
- Car 객체가 랜덤 확률이 아닌 다른 로직으로 이동할 땐?
- 랜덤 확률을 이용하는 다른 이동수단 객체(Boat, Airplane)가 생길 땐?
- **객체 끌어오지 말고 메세지만 보내라**
- "현관 비밀번호 바꿔라"를 기억하자
- 의미 없는 주석 달지 않기
- 메소드, 변수 명을 더 상세하게 달자
- 문자열, 숫자를 하드코딩 하지 말자
- static final 로 상수처리
- ~~상수전용 클래스 생성을 고려해보자~~
- 상수는 사용되는 클래스에 있는게 좋아보인다
- 구현 순서도 convention 이다
```java
class A {
1. 상수(static final) 또는 클래스 변수
2. 인스턴스 변수
3. 생성자
4. 메소드
}
```
- 일급 컬렉션을 공부하고, 적용해보자
- 단순한 데이터도 최대한 포장하자
- 검증은 View보다 해당 객체 생성자에서 진행하자

<br>

## 🚀 기능 요구사항
- 주어진 횟수 동안 n대의 자동차는 전진 또는 멈출 수 있다.
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/racingcar/Application.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@
import java.util.Scanner;

public class Application {

public static void main(String[] args) {
final Scanner scanner = new Scanner(System.in);
// TODO 구현 진행
Racing racing = new Racing();
racing.start(scanner);
}
}
65 changes: 64 additions & 1 deletion src/main/java/racingcar/Car.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,75 @@
package racingcar;

import java.util.ArrayList;

public class Car {

private static final String ENTER_LINE = "\n";
private static final String WHITE_SPACE = " ";
private static final String NO_STRING = "";
private static final String COLON = " : ";
private static final String DASH = "-";
private static final int MAX_LENGTH = 5;
private static final int EMPTY = 0;

private final String name;
private final GasTank gasTank;
private int position = 0;

public Car(String name) {
private Car(String name) {
validateEmpty(name);
validateLength(name);
this.name = name;
this.gasTank = GasTank.randomChargeGasTank();
}

// 추가 기능 구현
public static Car newCar(String name) {
return new Car(name);
}

public void tryForward() {
if (gasTank.isEnoughGas()) {
position++;
}
}


private void validateEmpty(String name) {
if (name.length() == EMPTY || name.replaceAll(WHITE_SPACE, NO_STRING).length() == EMPTY) {
throw new IllegalArgumentException("자동차 이름을 입력해주세요.");
}
}

private void validateLength(String name) {
if (name.length() > MAX_LENGTH) {
throw new IllegalArgumentException("각 자동차의 이름은 5자 이하여야 합니다.");
}
}

public void currentPosition(StringBuilder positions) {
positions.append(name);
positions.append(COLON);
positions.append(convertPosition());
positions.append(ENTER_LINE);
}

private StringBuilder convertPosition() {
StringBuilder position = new StringBuilder();
for (int i = 0; i < this.position; i++) {
position.append(DASH);
}
return position;
}

public int award(ArrayList<String> winners, int max_distance) {
if (max_distance < position) {
winners.clear();
max_distance = position;
}
if (max_distance == position) {
winners.add(name);
}
return max_distance;
}
}
77 changes: 77 additions & 0 deletions src/main/java/racingcar/Cars.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package racingcar;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

public class Cars {

private static final String WHITE_SPACE = " ";
private static final String NO_STRING = "";
private static final String COMMA = ",";
private static final char CHAR_COMMA = ',';
private static final int EMPTY = 0;
private static final int INIT_ZERO = 0;

private final ArrayList<Car> cars;

private Cars(String carNames) {
validateEmpty(carNames);
validateCommaCount(carNames);
validateDuplicate(carNames);
this.cars = enrollCars(carNames);
}

public static Cars newCars(String carNames) {
return new Cars(carNames);
}


private void validateEmpty(String carNames) {
if (carNames.replaceAll(WHITE_SPACE, NO_STRING).length() == EMPTY) {
throw new IllegalArgumentException("자동차 이름을 입력해주세요.");
}
}


private void validateCommaCount(String carNames) {
if (carNames.charAt(carNames.length() - 1) == CHAR_COMMA) {
throw new IllegalArgumentException("자동차 이름이 충분히 입력되지 않았습니다.");
}
}

private void validateDuplicate(String carNames) {
ArrayList<String> listCarNames = new ArrayList<>(Arrays.asList(carNames.split(COMMA)));
Set<String> setCarNames = new HashSet<>(Arrays.asList(carNames.split(COMMA)));
if (listCarNames.size() != setCarNames.size()) {
throw new IllegalArgumentException("중복된 이름은 안됩니다.");
}
}

private ArrayList<Car> enrollCars(String carNames) {
ArrayList<Car> cars = new ArrayList<>();
for (String carName : carNames.split(COMMA)) {
cars.add(Car.newCar(carName));
}
return cars;
}

public StringBuilder continueRacing() {
StringBuilder positions = new StringBuilder();
for (Car car : cars) {
car.tryForward();
car.currentPosition(positions);
}
return positions;
}

public ArrayList<String> awardWinners() {
ArrayList<String> winners = new ArrayList<>();
int max_distance = INIT_ZERO;
for (Car car : cars) {
max_distance = car.award(winners, max_distance);
}
return winners;
}
}
21 changes: 21 additions & 0 deletions src/main/java/racingcar/GasTank.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package racingcar;

import utils.RandomUtils;

public class GasTank {

private static final int MIN_GAS = 0;
private static final int MAX_GAS = 9;
private static final int ENOUGH_GAS = 4;

private GasTank() {
}

public static GasTank randomChargeGasTank() {
return new GasTank();
}

public boolean isEnoughGas() {
return RandomUtils.nextInt(MIN_GAS, MAX_GAS) >= ENOUGH_GAS;
}
}
37 changes: 37 additions & 0 deletions src/main/java/racingcar/Lap.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package racingcar;

public class Lap {

private static final int ZERO = 0;

private final int lap;
private int currentLap = 0;

private Lap(String lap) {
int validateLap = validateInteger(lap);
validateOverZero(validateLap);
this.lap = validateLap;
}

public static Lap newLap(String lap) {
return new Lap(lap);
}

private int validateInteger(String lap) {
try {
return Integer.parseInt(lap);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("시도 횟수는 숫자여야 합니다.");
}
}

private void validateOverZero(int lap) {
if (lap <= ZERO) {
throw new IllegalArgumentException("시도 횟수는 1 이상이어야 합니다.");
}
}

public boolean isEnd() {
return currentLap++ == lap;
}
}
39 changes: 39 additions & 0 deletions src/main/java/racingcar/Racing.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package racingcar;

import java.util.Scanner;

public class Racing {

public void start(Scanner scanner) {
Cars cars = entryCars(scanner);
Lap lap = entryLap(scanner);

View.printStartRacing();
while (!lap.isEnd()) {
View.printRacing(cars.continueRacing());
}
View.printWinners(cars.awardWinners());
}

private Cars entryCars(Scanner scanner) {
while (true) {
try {
View.printPleaseInputCars();
return Cars.newCars(scanner.nextLine());
} catch (IllegalArgumentException e) {
View.printError(e.getMessage());
}
}
}

private Lap entryLap(Scanner scanner) {
while (true) {
try {
View.printPleaseInputLap();
return Lap.newLap(scanner.nextLine());
} catch (IllegalArgumentException e) {
View.printError(e.getMessage());
}
}
}
}
Loading