Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
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
77 changes: 77 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,80 @@

자동차 경주 미션 저장소

## 설정 사항

- (중요) JDK 17 버전으로 진행한다.

## **기능 요구사항**

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

## **실행 결과**

- 위 요구사항에 따라 3대의 자동차가 5번 움직였을 경우 프로그램을 실행한 결과는 다음과 같다.

```
경주할 자동차 이름을 입력하세요(이름은 쉼표(,)를 기준으로 구분).
kokodak,kuku,cucu
시도할 회수는 몇회인가요?
5
실행 결과
kokodak : -
kuku : -
cucu : -
kokodak : --
kuku : -
cucu : --
kokodak : ---
kuku : --
cucu : ---
kokodak : ----
kuku : ---
cucu : ----
kokodak : -----
kuku : ----
cucu : -----
kokodak : -----
kuku : ----
cucu : -----
kokodak, cucu가 최종 우승했습니다.
```

## **프로그래밍 요구사항**

- **모든 로직에 단위 테스트를 구현한다. 단, UI(System.out, System.in) 로직은 제외**
- **자바 코드 컨벤션을 지키면서 프로그래밍한다.**
- 참고문서: https://google.github.io/styleguide/javaguide.html 또는 https://myeonguni.tistory.com/1596
- **`규칙 1: 한 메서드에 오직 한 단계의 들여쓰기(indent)만 한다.`**를 지키며 구현한다.
- 예를 들어 while문 안에 if문이 있으면 들여쓰기는 2이다.
- 힌트: indent(인덴트, 들여쓰기) depth를 줄이는 좋은 방법은 함수(또는 메소드)를 분리하면 된다.
- **`규칙 2: else 예약어를 쓰지 않는다.`**를 지키며 구현한다.
- 힌트: if 조건절에서 값을 return하는 방식으로 구현하면 else를 사용하지 않아도 된다.
- else를 쓰지 말라고 하니 switch/case로 구현하는 경우가 있는데 switch/case도 허용하지 않는다.
- **함수(또는 메소드)의 길이가 15라인을 넘어가지 않도록 구현한다.**
- 함수(또는 메소드)가 한 가지 일만 잘 하도록 구현한다.

**MVC 패턴으로 리팩터링 후의 main 메소드 예시**

```java
import view.InputView;
import view.ResultView;
import domain.RacingGame;

public class RacingMain {
public static void main(final String... args) {
final var carNames = InputView.getCarNames();
final var tryCount = InputView.getTryCount();

final var racingGame = new RacingGame(carNames, tryCount);
racingGame.race();

ResultView.printWinners(racingGame.getWinners());
}
}
```
42 changes: 42 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# domain
- ## Car(자동차) ##
- 이름
- 전진 거리

- ## CarName(자동차 이름) ##
- 미입력 or 5글자 초과 확인

- ## Distance(이동 거리) ##
- 정지 or 거리 증가

- ## Cars(자동차 리스트) ##
- 자동차들의 정보(중복 이름 X)
- 우승자 판별

- ## TryConut(시도 횟수) ##
- 음수 입력 X (횟수 상한은 없나??)

# ~~dto~~

# controller
- 이름 입력받아서 Cars로 저장
- 시도 횟수 입력받아 TryCount로 저장
- 레이스 진행
- 결과 출력

# utils
- ## Parser ##
- 이름: 쉼표 기준으로 분리
- 시도 횟수

- ## RandomNumberGenerator ##
- 0~9 랜덤 숫자 생성기

# view
- ## InputView ##
- 자동차 이름 입력
- 시도 횟수 입력

- ## OutView ##
- 실행 결과 출력
- 우승자 출력
5 changes: 4 additions & 1 deletion src/main/java/RacingMain.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import controller.RacingCarController;

public class RacingMain {

public static void main(String[] args) {
// TODO: MVC 패턴을 기반으로 자동차 경주 미션 구현해보기
System.out.println("Hello, World!");
RacingCarController racingCarController = new RacingCarController();
racingCarController.run();
}
}
66 changes: 66 additions & 0 deletions src/main/java/controller/RacingCarController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package controller;

import domain.Car;
import domain.Cars;
import domain.TryCount;
import utils.RandomNumberGenerator;
import view.InputView;
import view.OutputView;

import java.io.IOException;
import java.util.List;

public class RacingCarController {

private final RandomNumberGenerator randomNumberGenerator;

public RacingCarController() {
this.randomNumberGenerator = new RandomNumberGenerator();

Choose a reason for hiding this comment

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

혹시controller 생성자에서 random하게 0~9까지의 수를 결정해 제공하는 클라스 객체를 생성한 이유가 있으신지 궁금합니다!

}

public void run() {
Cars cars = getCars();
TryCount tryCount = getTryCount();
race(cars, tryCount);
printWinner(cars);
}

private Cars getCars() {
List<String> carNames = InputView.readCarNames();
try {
return new Cars(carNames);
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
return getCars(); //올바른 입력 넣을 때까지 반복

Choose a reason for hiding this comment

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

저는 올바른 입력(차 이름 또는 반복 횟수)이 오지 않았을 때 바로 프로그램을 종료하게 코드를 작성했는데 계속 반복적으로 제대로 입력될 때 까지 받게 코드 작성하신 부분에서 저랑 좀 차이가 있는 것 같습니다

}
}

private TryCount getTryCount() {
try {
int number = InputView.readTryCount();
return new TryCount(number);
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
return getTryCount(); //올바른 입력 넣을 때까지 반복
}
}

private void race(Cars cars, TryCount tryCount) {
OutputView.printResult();
while (tryCount.isRemain()) {
cars.moveCars(randomNumberGenerator);
printStatus(cars);
tryCount.decrease();
}
}

private void printStatus(Cars cars) {
List<Car> carList = cars.getCars();
OutputView.printStatus(carList);
}

private void printWinner(Cars cars) {
List<Car> winnerList = cars.findWinner();
OutputView.printWinners(winnerList);
}
}
28 changes: 28 additions & 0 deletions src/main/java/domain/Car.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package domain;

public class Car {

private static final int CAN_MOVE_NUMBER = 4;
private final CarName carName;
private final Distance distance;


public Car(String carName) {
this.carName = new CarName(carName);
this.distance = new Distance();
}

public void move(int number) {
if (number >= CAN_MOVE_NUMBER) {
distance.increaseDistance();
}
}

public String getCarName() {
return carName.getCarName();
}

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

public class CarName {
private final String carName;

public CarName(String carName) {
validateCarName(carName);
this.carName = carName;
}

public String getCarName() {
return carName;
}

private void validateCarName(String carName) { //이름 글자수 확인(없거나, 5글자 초과일 시)
if (carName.isEmpty()) {
throw new IllegalArgumentException("이름이 반드시 존재해야 합니다.");
}
if (carName.length() > 5) {
throw new IllegalArgumentException("이름은 5글자 이하여야 합니다.");
}
}
}
62 changes: 62 additions & 0 deletions src/main/java/domain/Cars.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package domain;

import utils.RandomNumberGenerator;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

public class Cars {

private static final String DUPLICATED_NAMES_MASSAGE= "자동차의 이름은 중복될 수 없습니다.";
private static final int DEFAULT_DISTANCE = 0;
private final List<Car> cars;

public Cars(List<String> carNames) {
final List<Car> cars= generateCars(carNames);
validateDuplication(carNames);
this.cars = cars;
}

private List<Car> generateCars(List<String> cars) {
return cars.stream()
.map(Car::new)
.collect(Collectors.toList());
}

public void moveCars(RandomNumberGenerator randomNumberGenerator) {
for (Car car : cars) {
int randomNumber = randomNumberGenerator.generate();
car.move(randomNumber);
}
}

// 이름 중복 확인
private void validateDuplication(List<String> carNames) {
Set<String> duplicationCheck = new HashSet<>(carNames);
if (carNames.size() != duplicationCheck.size()) {
throw new IllegalArgumentException(DUPLICATED_NAMES_MASSAGE);
}
}

// 최대 이동 거리 구하기
private int findMaxDistance() {
return cars.stream()
.mapToInt(Car::getDistance)
.max()
.orElse(DEFAULT_DISTANCE);
}

// 최대 이동 거리와 같은 위치에 있는 자동차들 찾기
public List<Car> findWinner() {
int maxDistance = findMaxDistance();
return cars.stream()
.filter(car -> car.getDistance() == maxDistance)
.collect(Collectors.toList());
}

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

public class Distance {

private static final int INITIAL_VALUE = 0;
private int distance;

public Distance() {
this.distance = INITIAL_VALUE;
}

public void increaseDistance() {
distance++;
}

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

public class TryCount {

private static final String NOT_POSITIVE_INTEGER_MESSAGE = "시도 횟수는 양의 정수여야 합니다";
Copy link
Member

Choose a reason for hiding this comment

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

에러 메세지는 Enum Class를 사용해서 따로 모아두는 방법도 좋을 것 같아요!

private static final int MINIMUM_COUNT = 1;

private int tryCount;

public TryCount(int tryCount) {
validate(tryCount);
this.tryCount = tryCount;
}

private void validate(int tryCount) {
if (tryCount <= 0) {
throw new IllegalArgumentException(NOT_POSITIVE_INTEGER_MESSAGE);
}
}

public void decrease() {
tryCount--;
}

public boolean isRemain() {
return tryCount >= MINIMUM_COUNT;
}

public int getTryCount() {
return tryCount;
}
}
23 changes: 23 additions & 0 deletions src/main/java/utils/Parser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package utils;

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

public class Parser {

private static final String NOT_POSITIVE_INTEGER = "시도 횟수는 정수여야 합니다";
public static List<String> parseNames(String input) {
return Arrays.stream(input.split(","))
.map(String::trim)

Choose a reason for hiding this comment

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

저는 공백을 포함하는 이름이 나왔을 때는 오류 처리를 하게 하였는데(이름 중간에 공백 나오면 에러) trim을 통해 이름 앞 뒤 공백을 없애는 방법으로 코드 작성하는 방식이 예외 처리 허용 부분에서저랑 좀 다르게 작성한 것 같습니다..!

.collect(Collectors.toList());
}

public static int parseCount(String input) {
try {
return Integer.parseInt(input);
} catch (NumberFormatException e) {
throw new IllegalArgumentException(NOT_POSITIVE_INTEGER);
}
}
}
Loading