Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
79 commits
Select commit Hold shift + click to select a range
c211692
feat: InputMessage 구현
nove1080 May 31, 2024
df75c0c
feat: OutputMessage 구현
nove1080 May 31, 2024
988e13e
feat: InputMessage toString() 오버라이딩
nove1080 Jun 4, 2024
9fb7c9a
feat: OutputMessage toString() 오버라이딩
nove1080 Jun 4, 2024
2e08c62
feat: InputView 선언
nove1080 Jun 4, 2024
eb3118c
style: InputView RACING_CAR_NAME 이름 변경
nove1080 Jun 4, 2024
d16b7e8
feat: CarNameValidator 선언
nove1080 Jun 5, 2024
105a6ae
refactor: CarNameValidator 사용하지 않는 메서드 삭제
nove1080 Jun 5, 2024
81b6205
feat: ErrorMessage 구현
nove1080 Jun 5, 2024
c2ad090
feat: CarNameValidator 싱글턴 적용
nove1080 Jun 5, 2024
b5e3af6
feat: CarNameValidator 이름 길이 검증 메서드 추가
nove1080 Jun 5, 2024
837a744
feat: ErrorMessage 중복 이름에 대한 메시지 추가
nove1080 Jun 5, 2024
a63f15c
feat: CarNameValidator 중복 이름 검증 메서드 추가
nove1080 Jun 5, 2024
1c85f30
feat: Rule 상수 클래스 추가
nove1080 Jun 5, 2024
39e0ec5
test: CarNameValidatorTest 추가
nove1080 Jun 6, 2024
434e028
feat: InputView 자동차 이름 입력 메서드 추가
nove1080 Jun 6, 2024
8ecaee9
feat: ErrorMessage [ERROR] 라벨 적용
nove1080 Jun 6, 2024
a56fee6
feat: ErrorMessage 시도 횟수와 관련된 예외 메시지 추가
nove1080 Jun 6, 2024
4ffb742
feat: AttemptCountValidator 구현
nove1080 Jun 6, 2024
e724efb
feat: InputView 시도 횟수 입력 메서드 추가
nove1080 Jun 6, 2024
e77fd91
style: CarNameValidatorTest DisplayName 마침표 제거
nove1080 Jun 6, 2024
86a6981
test: AttemptCountValidatorTest 테스트 코드 구현
nove1080 Jun 6, 2024
0654871
feat: Rule 자동자 전진 조건 상수 추가
nove1080 Jun 6, 2024
0b835d6
feat: Rule 자동차 위치 출력 마커 추가
nove1080 Jun 6, 2024
27efd28
feat: Car 클래스 구현
nove1080 Jun 7, 2024
ef1c182
feat: Rule 난수 생성의 최대 범위 추가
nove1080 Jun 7, 2024
f6ed02c
feat: Car 클래스 생성자 추가
nove1080 Jun 8, 2024
bfd7ace
feat: Cars 구현
nove1080 Jun 8, 2024
1e1c9f0
feat: Cars move() 추가
nove1080 Jun 8, 2024
316b1da
feat: Cars 우승자를 찾는 메서드 추가
nove1080 Jun 8, 2024
44f3bb5
test: CarTest 자동차 움직임에 대한 테스트 추가
nove1080 Jun 8, 2024
86a29a7
feat: Car move와 canMove 오버로딩
nove1080 Jun 8, 2024
9a7e9f3
feat: Car equalsAndHashcode, toString 오버라이딩
nove1080 Jun 8, 2024
d72d493
refactor: Car의 파라미터가 없는 move와 canMove 메서드 삭제
nove1080 Jun 8, 2024
16f3337
test: CarsTest findWinner()에 대한 테스트 추가
nove1080 Jun 8, 2024
16a4bbb
refactor: Cars의 findWinner() 리턴 타입 변경
nove1080 Jun 8, 2024
2af05dc
refactor: Cars의 move() 로직 수정
nove1080 Jun 8, 2024
a4685ed
feat: Cars에 getCarList() 추가
nove1080 Jun 8, 2024
da384d2
feat: Cars에 equalsAndHashcode 오버라이딩
nove1080 Jun 8, 2024
e252a77
feat: Cars에 toString 오버라이딩
nove1080 Jun 8, 2024
9e41f04
refactor: Cars의 findWinners 승자 반환 코드 수정
nove1080 Jun 8, 2024
53173ef
test: CarsTest getCarList 메서드 이름 변경
nove1080 Jun 8, 2024
7014f3b
test: CarsTest Cars.getMaxPosition() 에 대한 테스트 추가
nove1080 Jun 8, 2024
520d711
feat: Car isWinner(int) 추가
nove1080 Jun 8, 2024
6d6f754
feat: Cars 승자 판별 로직 중 filter 수정
nove1080 Jun 8, 2024
ee9a35a
test: CarTest movableCar 테스트 코드 수정
nove1080 Jun 8, 2024
0ab2067
test: CarTest 위치가 제자리인 경우 검증 시 isZero() 메서드 적용
nove1080 Jun 8, 2024
7546811
feat: Cars 정적 팩토리 메서드 from() 추가
nove1080 Jun 8, 2024
6f8adf5
refactor: InputView enterCarNames() 정상 입력까지 수행하도록 수정
nove1080 Jun 8, 2024
9782fa5
refactor: InputView enterAttempCount() 정상 입력까지 수행하도록 수정
nove1080 Jun 9, 2024
bde7ff7
feat: RandomNumberGenerator 추가
nove1080 Jun 9, 2024
8befc75
refactor: Cars Random 에 대한 의존성 제거
nove1080 Jun 9, 2024
9bad140
feat: ErrorMessage EMPTY_LIST 문구 추가
nove1080 Jun 9, 2024
d9f0dc1
refactor: Cars 생성자 대신 정적 팩토리 메서드를 제공하도록 수정
nove1080 Jun 9, 2024
40e321b
test: Cars 객체 생성을 정적 팩토리 메서드를 이용하도록 수정
nove1080 Jun 9, 2024
740dc9a
docs: RandomNumberGenerator getNumber 주석 수정
nove1080 Jun 9, 2024
5e9a16c
refactor: RandomNumberGenerator Random 객체 필드로 선언
nove1080 Jun 9, 2024
f4e2fa2
refactor: CarNameValidator 정적 클래스로 변경
nove1080 Jun 9, 2024
dac0825
refactor: AttemptCountValidator 정적 클래스로 변경
nove1080 Jun 9, 2024
a949771
test: CarNameValidatorTest 검증기가 정적 클래스로 변경됨에 따른 수정
nove1080 Jun 9, 2024
a4dadbe
test: AttemptCountValidatorTest 검증기가 정적 클래스로 변경됨에 따른 수정
nove1080 Jun 9, 2024
d0d2658
refactor: Car의 검증기가 정적 클래스로 변경됨에 따른 수정
nove1080 Jun 9, 2024
8395c5f
refactor: InputView의 검증기가 정적 클래스로 변경됨에 따른 수정
nove1080 Jun 9, 2024
1c0a3a0
feat: OutputView 추가
nove1080 Jun 9, 2024
41f4a6e
feat: OutputView printWinner 추가
nove1080 Jun 9, 2024
2ba2cf6
feat: RacingGameController 구현
nove1080 Jun 9, 2024
029353c
feat: RacingGameApplication 구현
nove1080 Jun 9, 2024
998a1a7
test: AttemptCountValidatorTest 예외 검증 메서드 변경
nove1080 Jun 9, 2024
22414ba
feat: Rule MIN_NAME_LENGTH 추가
nove1080 Jun 9, 2024
23c6bd1
feat: Rule final 키워드 추가
nove1080 Jun 9, 2024
9a4d967
refactor: ErrorMessage 이름 길이 예외 메시지 변수명 수정
nove1080 Jun 9, 2024
b789065
test: CarNameValidatorTest 테스트 삭제
nove1080 Jun 9, 2024
a8a7793
test: CarNameValidatorTest 예외 검증 메서드 변경
nove1080 Jun 9, 2024
d2800cf
feat: CarNameValidator validateNameExists 추가
nove1080 Jun 9, 2024
02bab40
refactor: AttemptCountValidatorTest 사용하지 않는 import 삭제
nove1080 Jun 9, 2024
5f31536
refactor: CarTest 사용하지 않는 import 삭제
nove1080 Jun 9, 2024
fb66105
feat: Cars getCarNames 추가
nove1080 Jun 9, 2024
3a12359
refactor: OutputView printWinner 로직 수정
nove1080 Jun 9, 2024
cd287d8
docs: README.md 작성
nove1080 Jun 9, 2024
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
93 changes: 92 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,92 @@
# java-racingcar-precourse
# java-racingcar-precourse
***
## 패키지 구조
***
```
├── RacingGameApplication.java
├── config
│   └── constant
│   └── Rule.java
├── controller
│   └── RacingGameController.java
├── domain
│   ├── Car.java
│   └── Cars.java
├── exception
│   └── constant
│   └── ErrorMessage.java
├── util
│   └── RandomNumberGenerator.java
├── validator
│   ├── AttemptCountValidator.java
│   └── CarNameValidator.java
└── view
├── InputView.java
├── OutputView.java
└── constant
├── InputMessage.java
└── OutputMessage.java
```

### config 패키지
> 게임 설정과 관련된 파일이 위치합니다.
* `Rule.java`
* 레이싱 게임을 진행함에 있어 지켜야 할 규칙에 대해 상수로 정의합니다.

### controller 패키지
> Model과 View를 이어주는 Controller가 위치합니다.
* `RacingGameController.java`
* 레이싱 게임을 진행하기 위한 컨트롤러

### domain 패키지
> 레이싱 게임을 구성하기 위한 도메인이 위치합니다.
* `Car.java`
* 레이싱 게임에 참여하는 한 대의 자동차를 의미합니다.


* `Cars.java`
* 레이싱 게임에 참여하는 자동차들의 집합을 의미합니다.
* 자동차의 집합 중 승자를 판단할 수 있습니다.

### exception 패키지
> 레이싱 게임 진행 중 발생하는 예외에 대한 정보가 위치합니다.
* `ErrorMessage.java`
* 게임 중 발생하는 예외 메시지에 대해 정의합니다.

### util 패키지
> 게임 진행에 필요한 유틸리티 클래스들이 위치합니다.
* `RandomNumberGenerator.java`
* 자동차를 움직이기 위한 난수를 발생시킵니다.

### validator 패키지
> 게임에 사용되는 검증기가 위치합니다.
* `AttemptCountValidator.java`
* 레이싱 실행 횟수에 대하여 검증합니다.
* 입력값이 숫자가 맞는지 검증합니다.
* 입력값이 양의 정수가 맞는지 검증합니다.


* `CarNameValidator.java`
* 자동차 이름에 대한 검증을 실시합니다.
* 이름이 존재하는지 검증합니다.
* 이름의 길이가 적절한지 검증합니다.
* 이름에 빈 문자열이 있는지 검증합니다.
* 이름 입력에 대해 중복이 존재하는지 검증합니다.

### view 패키지
> view 계층이 위치합니다.
* `InputView.java`
* 입력에 필요한 내용을 출력합니다.
* 적절한 입력이 발생할 때까지 사용자로부터 입력을 받습니다.


* `OutputView.java`
* 게임의 결과에 대해 출력합니다.


* `InputMessage.java`
* 입력에 필요한 메시지를 정의합니다.


* `OutputMessage.java`
* 출력에 필요한 메시지를 정의합니다.
18 changes: 18 additions & 0 deletions src/main/java/game/RacingGameApplication.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package game;

import game.controller.RacingGameController;
import game.view.InputView;
import game.view.OutputView;
import java.util.Scanner;

public class RacingGameApplication {

public static void main(String[] args) {
InputView inputView = new InputView(new Scanner(System.in));
OutputView outputView = new OutputView();
RacingGameController racingGameController = new RacingGameController(inputView, outputView);

racingGameController.startGame();
}

}
14 changes: 14 additions & 0 deletions src/main/java/game/config/constant/Rule.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package game.config.constant;

public class Rule {

private Rule() {
}

public static final String NAME_DELIMITER = ",";
public static final int MAX_NAME_LENGTH = 5;
public static final int MIN_NAME_LENGTH = 1;
public static final int MAX_RANDOM_VALUE = 9;
public static final int MOVEMENT_THRESHOLD = 4;
public static final String CAR_POSITION_MAKER = "-";
}
33 changes: 33 additions & 0 deletions src/main/java/game/controller/RacingGameController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package game.controller;

import game.domain.Cars;
import game.view.InputView;
import game.view.OutputView;
import game.view.constant.OutputMessage;
import java.util.List;

public class RacingGameController {

private InputView inputView;
private OutputView outputView;

public RacingGameController(InputView inputView, OutputView outputView) {
this.inputView = inputView;
this.outputView = outputView;
}

public void startGame() {
List<String> carNames = inputView.enterCarNames();
Cars cars = Cars.fromCarNames(carNames);

int attemptCount = inputView.enterAttemptCount();
System.out.println(OutputMessage.RESULT);
for (int i = 0; i < attemptCount; i++) {
cars.move();
outputView.printResult(cars);
}

outputView.printWinner(cars);
}

}
69 changes: 69 additions & 0 deletions src/main/java/game/domain/Car.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package game.domain;

import game.config.constant.Rule;
import game.validator.CarNameValidator;
import java.util.Objects;

public class Car {

private final String name;
private int position;

public Car(String name) {
this(name, 0);
}

public Car(String name, int position) {
CarNameValidator.validateNameLength(name);
this.name = name;
this.position = position;
}

public String getName() {
return name;
}

public int getPosition() {
return position;
}

public int move(int randomValue) {
if(canMove(randomValue)) {
this.position++;
}
return this.position;
}

private boolean canMove(int randomValue) {
return randomValue >= Rule.MOVEMENT_THRESHOLD;
}

public boolean isWinner(int maxPosition) {
return position == maxPosition;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Car car = (Car) o;
return position == car.position && Objects.equals(name, car.name);
}

@Override
public int hashCode() {
return Objects.hash(name, position);
}

@Override
public String toString() {
return "Car{" +
"name='" + name + '\'' +
", position=" + position +
'}';
}
}
91 changes: 91 additions & 0 deletions src/main/java/game/domain/Cars.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package game.domain;

import game.config.constant.Rule;
import game.exception.constant.ErrorMessage;
import game.util.RandomNumberGenerator;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Objects;

public class Cars {

private List<Car> carList;

private Cars(List<Car> carList) {
this.carList = carList;
}

public static Cars fromCarNames(List<String> carNames) {
return new Cars(carNames.stream().map(Car::new).toList());
}

public static Cars fromCarList(List<Car> carList) {
return new Cars(carList);
}

public void move() {
carList.forEach(car -> car.move(RandomNumberGenerator.getNumber(Rule.MAX_RANDOM_VALUE)));
}

public Cars findWinners() {
int maxPosition = getMaxPosition();
List<Car> winners = carList.stream()
.filter(car -> car.isWinner(maxPosition))
.toList();
return new Cars(winners);
}

public int getMaxPosition() {
return carList.stream()
.map(Car::getPosition)
.max(Comparator.naturalOrder())
.orElseThrow(() -> new NoSuchElementException(ErrorMessage.EMPTY_LIST.getMessage()));
}

public List<String> getCarNames() {
return carList.stream().map(Car::getName).toList();
}

public List<Car> getCarList() {
return Collections.unmodifiableList(carList);
}

/**
* 리스트의 순서와 상관없이 내부 원소만 비교하여 동등성을 판단합니다
*
* @param o 비교할 Cars 인스턴스
* @return 동등여부
*/
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Cars cars = (Cars) o;
return new HashSet<>(carList).equals(new HashSet<>(cars.getCarList()));
}

@Override
public int hashCode() {
return Objects.hash(carList);
}

@Override
public String toString() {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("Cars = ");
carList.forEach(car ->
stringBuilder.append("(")
.append(car.getName())
.append(", position: ")
.append(car.getPosition())
.append(")"));
return stringBuilder.toString();
}
}
22 changes: 22 additions & 0 deletions src/main/java/game/exception/constant/ErrorMessage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package game.exception.constant;

import static game.config.constant.Rule.*;

public enum ErrorMessage {

INVALID_NAME_LENGTH("[ERROR] %d ~ %d 글자를 벗어난 이름이 존재합니다!".formatted(MIN_NAME_LENGTH, MAX_NAME_LENGTH)),
DUPLICATE_NAME_FOUND("[ERROR] 중복된 이름이 존재합니다!"),
INVALID_NUMBER_INPUT("[ERROR] 숫자를 입력해주세요!"),
INVALID_ATTEMPT_COUNT_INPUT("[ERROR] 1 이상의 숫자를 입력해주세요!"),
EMPTY_LIST("[ERROR] 리스트가 비어 있습니다!");
private final String message;

ErrorMessage(String message) {
this.message = message;
}

public String getMessage() {
return message;
}

}
21 changes: 21 additions & 0 deletions src/main/java/game/util/RandomNumberGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package game.util;

import java.util.Random;

public class RandomNumberGenerator {

private static Random random = new Random();

private RandomNumberGenerator() {
}

/**
* 파라미터로 전달받은 범위 안에서 무작위 음이 아닌 정수를 반환합니다.
* @param maxRange 난수의 최댓값
* @return 음이 아닌 정수
*/
public static int getNumber(int maxRange) {
return random.nextInt(maxRange + 1);
}

}
Loading