Skip to content
Open
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
69d278c
feat: Car 클래스 작성
JanooGwan Sep 22, 2025
9229ec4
feat: 게임 정보 입력 & 관련 DTO 작성
JanooGwan Sep 22, 2025
685fe5a
feat: 레이스 상황 출력 관련 DTO 작성
JanooGwan Sep 22, 2025
40da36d
feat: 레이스 최종 결과 출력 관련 DTO 작성
JanooGwan Sep 22, 2025
61d357e
refactor: Car 클래스에 static 멤버 변수 추가
JanooGwan Sep 22, 2025
7bbb90e
feat: InputView & OutputView 작성
JanooGwan Sep 22, 2025
f64079a
feat: StringUtils 작성
JanooGwan Sep 22, 2025
a7ff546
refactor: StringUtils - 문자열을 이용해 Car들을 만드는 메소드 추가
JanooGwan Sep 22, 2025
45ea126
refactor: OutputView - 경기 상황, 경기 결과 출력 메소드 추가
JanooGwan Sep 22, 2025
37fa75d
feat: RacingController 작성
JanooGwan Sep 22, 2025
3225b97
feat: RaceManager 작성
JanooGwan Sep 22, 2025
8b31444
refactor: RacingController, RaceManager 수정
JanooGwan Sep 22, 2025
50e6030
refactor: InputView 수정
JanooGwan Sep 22, 2025
279705a
feat: CheckCarName 클래스 작성
JanooGwan Sep 22, 2025
a2c3577
refactor: CheckCarName 수정
JanooGwan Sep 22, 2025
8d25826
refactor: InputView 수정
JanooGwan Sep 22, 2025
d728091
feat: CheckTryCount 클래스 작성
JanooGwan Sep 22, 2025
7da49d6
refactor: RacingController - 유효성 검증 관련 수정
JanooGwan Sep 22, 2025
84f9b6f
style: 불필요한 공백 제거
JanooGwan Sep 26, 2025
7313a94
refactor: 자동차 이름 검증 로직 - 생성자 영역으로 이동
JanooGwan Sep 26, 2025
7fba792
style: 접근 제어자(private) 추가
JanooGwan Sep 26, 2025
d7231b8
refactor: 유효성 검증 관련 클래스 이름 변경(Validator)
JanooGwan Sep 26, 2025
cb66dfc
style: 불필요한 공백 제거
JanooGwan Sep 26, 2025
014e73c
refactor: Iterator 관련 부분 Stream을 활용한 형태로 변경
JanooGwan Sep 26, 2025
f1e0633
refactor: StringUtils - Stream을 활용한 형태로 변경
JanooGwan Sep 26, 2025
2ac8837
refactor: RacingController의 생성자 - 외부에서 객체를 주입받도록 변경
JanooGwan Sep 26, 2025
4a8e1f0
feat: DTO - RaceInfoRequest 작성
JanooGwan Sep 28, 2025
a96b0a3
feat: DTO - RaceInfoResponse 작성
JanooGwan Sep 28, 2025
b6e5763
refactor: StringUtils - DTO 변경으로 인한 수정
JanooGwan Sep 28, 2025
903d754
refactor: StringUtils 반환 타입 변경 및 관련 코드 수정
JanooGwan Sep 28, 2025
12502f4
refactor: RacingController - RaceInfoRequest(DTO)를 활용한 형태로 변경
JanooGwan Sep 28, 2025
dcede87
refactor: StringUtils - String 기반 Car 생성 메소드 수정
JanooGwan Sep 29, 2025
b93d0f6
refactor: RacingController - StringUtils의 메소드를 활용하여 Car 생성하도록 변경
JanooGwan Sep 29, 2025
593e6cc
refactor: TryCountValidator - isNumber 메소드 추가
JanooGwan Sep 29, 2025
0b2d351
refactor: InputView - getNumInput() 삭제
JanooGwan Sep 29, 2025
69b3c01
refactor: RacingController - tryCount 관련 수정
JanooGwan Sep 29, 2025
dc2bbdd
style: 불필요한 공백 제거
JanooGwan Sep 29, 2025
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
30 changes: 30 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
## 구현해야 하는 기능 목록

### 자동차
- [x] 클래스 기본 틀 작성(변수, 생성자 등)
- [x] 랜덤 수(0~9) 반환 메소드
- [x] 자동차를 한 칸 전진하는 메소드

### 입력
- [x] 자동차 이름 & 입력 횟수 입력
- [x] 입력받은 데이터 전달

### 출력
- [x] 라운드 진행 시마다 경기 진행 상황 출력
- [x] 경주 종료 후 최종 결과 및 우승자 출력

### 예외 처리
- [x] 자동차 이름 관련
- [x] 시도 횟수 관련


### 고민 사항
- 자동차 정보를 입력받는 DTO를 어떻게 설계하는 것이 좋을까?
- 입력 텍스트 그대로(poni,woni,jun)를 DTO에 넣기
- 이름을 분리하여 List<String>에 넣기
- 이름을 분리한 것을 바탕으로 Car 객체들로 변환하고, List<Cars>에 넣기
- Util 패키지 관련
- 객체들을 new로 생성해서 사용하는 것이 좋은가?
- 아니면 객체를 별도로 생성하지 않고 객체 내부 메소드들을 모두 static 메소드로 만들어서 쓰는 것이 나은가?
- 시도 횟수를 입력할 때, 숫자가 아닌 다른 형태의 값을 입력했을 경우
- CheckTryCount 라는 별도의 클래스에서 이를 검증할 방법은 없을까?(현재 코드는 sc.nextInt() 하는 시점에 try-catch를 통해 예외처리 하고 있음)
8 changes: 7 additions & 1 deletion src/main/java/racingcar/Application.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
package racingcar;

import racingcar.controller.RacingController;
import racingcar.controller.dto.RaceInfo;

public class Application {
public static void main(String[] args) {
// TODO: 프로그램 구현
RacingController racingController = new RacingController();

RaceInfo raceInfo = racingController.getInfosBeforeRaceStart();
racingController.raceStart(raceInfo);
}
}
57 changes: 57 additions & 0 deletions src/main/java/racingcar/controller/RacingController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package racingcar.controller;

import racingcar.controller.dto.RaceInfo;
import racingcar.util.CheckCarName;
import racingcar.util.CheckTryCount;
import racingcar.util.RaceManager;
import racingcar.util.StringUtils;
import racingcar.model.Car;
import racingcar.view.InputView;
import racingcar.view.OutputView;

import java.util.List;


Copy link

Choose a reason for hiding this comment

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

여기에 불필요한 공백이 있는 것 같아요!

Copy link
Author

Choose a reason for hiding this comment

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

11번째 줄 공백을 말하신 건가요? 수정하겠습니다

Copy link

Choose a reason for hiding this comment

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

13, 14번째 줄을 얘기한거에요!
보통 두 줄 사이의 간격은 한 칸으로 유지해주세요!

public class RacingController {
private final InputView inputView;
private final OutputView outputView;
private final RaceManager raceManager;


Copy link

Choose a reason for hiding this comment

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

여기도 마찬가지로 확인해주세요

Copy link
Author

Choose a reason for hiding this comment

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

여기 같은 경우에는 필드 영역과 메소드 영역을 사이를 한칸 띄우는게 좀더 보기 편해서 이렇게 했는데,
혹시 이렇게 했을 경우 오히려 가독성을 해칠 수 있는 경우가 있을까요??

Copy link

Choose a reason for hiding this comment

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

영역을 나누는 것도 사이에 한 칸만 있으면 충분해 보입니다..!!

public RacingController() {
this.inputView = new InputView();
Copy link

Choose a reason for hiding this comment

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

추가로 여기 new 쪽에 공백 하나가 더 있어요!

Copy link
Author

Choose a reason for hiding this comment

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

헐 정말 꼼꼼하게 봐주시네요...! 수정하겠습니다

this.outputView = new OutputView();
this.raceManager = new RaceManager();
}
Copy link

Choose a reason for hiding this comment

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

생성자 내부에서 new를 통해 만든 객체를 주입해준다면, 나중에 어떠한 요구사항이 변경되었을 때 주입해야 할 객체의 수정이 어려울 것 같아요.
생성자에 인자를 추가하여 외부에서 주입받도록 하는 방식은 어떤가요?

Copy link
Author

Choose a reason for hiding this comment

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

생각해보니 위에 처럼 작성하면 유연성이 조금 떨어질 수 있을 것 같습니다.
이것도 말씀해주신 대로 수정해보도록 하겠습니다


public RaceInfo getInfosBeforeRaceStart() {
outputView.getCars();
String racerStr = inputView.getStringInput();
List<Car> cars = StringUtils.makeCarsUsingString(racerStr);
for(var c : cars) CheckCarName.checkName(c.getName());
Copy link

Choose a reason for hiding this comment

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

자동차 이름의 검증은 객체 생성 후에 하는 것이 아닌 객체의 생성자에서 진행하는건 어떤가요?
저는 생성자의 책임으로는 안전한 값을 지닌 객체를 생성하는 것이라 생각해서 생성자 내부에 이름의 유효성 검증 로직이 들어와야 한다고 생각해요.

Copy link
Author

Choose a reason for hiding this comment

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

검증 단계를 어느 위치에서 하는 것이 좋을까 고민을 많이 했었는데,
생각해보니 말씀해주신 것처럼 객체 생성 시점에 바로 검증하는 것이 좋을 것 같습니다

public Car(String name) {
CheckCarName.checkName(name);
this.name = name;
this.location = 0;
}

이런 식으로 수정해보려고 하는데, 이렇게 해도 문제 없을까요??

Copy link

Choose a reason for hiding this comment

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

👍👍👍


outputView.getTryCount();
int tryCnt = inputView.getNumInput();
CheckTryCount.isPositive(tryCnt);

return new RaceInfo(cars, tryCnt);
}

public void raceStart(RaceInfo raceInfo) {
List<Car> cars = raceInfo.cars();

outputView.printRaceStart();

for(int i = 0; i < raceInfo.gameCount(); ++i) {
raceManager.movingCars(cars);
outputView.printRaceStatus(cars);
}

finishRace(cars);
}

private void finishRace(List<Car> cars) {
List<String> winners = raceManager.findWinners(cars);
outputView.printRaceFinalStatus(winners);
}
}
12 changes: 12 additions & 0 deletions src/main/java/racingcar/controller/dto/RaceInfo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package racingcar.controller.dto;

import racingcar.model.Car;

import java.util.List;


Copy link

Choose a reason for hiding this comment

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

여기도 공백 확인해주세요.

public record RaceInfo(
List<Car> cars,
int gameCount
) {
}
30 changes: 30 additions & 0 deletions src/main/java/racingcar/model/Car.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package racingcar.model;

public class Car {
private static final int CAN_MOVE_STANDARD = 4;
Copy link

Choose a reason for hiding this comment

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

private 변수인데, static을 붙인 이유가 있으실까요??
만약 무의식적으로 붙이셨다면 static 변수의 특징을 조사해보시면 될 것 같아요

Copy link
Author

Choose a reason for hiding this comment

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

우선 private는, 자동차가 움직이는 기준 값을 외부에 공개할 필요가 없기 때문에 private를 사용하였고,
인스턴스 생성 때마다 CAN_MOVE_STANDARD 변수를 생성할 필요는 없다고 판단하여서, 클래스 내에서 공유할 수 있도록 static을 사용하였습니다.


int location;
String name;
Copy link

Choose a reason for hiding this comment

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

접근제어자가 빠진 것 같아요!

Copy link
Author

Choose a reason for hiding this comment

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

앗 수정하겠습니다!



public Car(String name) {
this.name = name;
this.location = 0;
}

public void move() {
if(isAbleToMove()) ++location;
}

private boolean isAbleToMove() {
return (int)(Math.random() * 10) >= CAN_MOVE_STANDARD;
}

public int getLocation() {
return location;
}

public String getName() {
return name;
}
}
26 changes: 26 additions & 0 deletions src/main/java/racingcar/util/CheckCarName.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package racingcar.util;

public class CheckCarName {
Copy link

@dh2906 dh2906 Sep 23, 2025

Choose a reason for hiding this comment

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

Check 라는 이름보다는 Validator가 더 적합해보여요.
관우님은 어떻게 생각하시나요?!

Copy link
Author

Choose a reason for hiding this comment

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

유효성을 검증하는 클래스의 마땅한 이름이 생각이 안나서 Check 를 사용했는데,
Validator 라고 이름 짓는게 더 명확해 보이는 것 같습니다. 수정하겠습니다

private static final int NAME_MAX_LENGTH = 5;

public static void isNull(String name) {
if(name == null)
throw new IllegalArgumentException("이름에는 null 값이 들어갈 수 없습니다.");
}

public static void isBlank(String name) {
if(name.isBlank())
throw new IllegalArgumentException("이름은 공백일 수 없습니다.");
}

public static void isMoreThanMaxLength(String name) {
if(name.length() > NAME_MAX_LENGTH)
throw new IllegalArgumentException("이름은 5자를 넘을 수 없습니다.");
}

public static void checkName(String name) {
isNull(name);
isBlank(name);
isMoreThanMaxLength(name);
}
}
8 changes: 8 additions & 0 deletions src/main/java/racingcar/util/CheckTryCount.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package racingcar.util;

public class CheckTryCount {
public static void isPositive(int tryCnt) {
if(tryCnt < 0)
throw new IllegalArgumentException("시도 횟수는 자연수여야 합니다.");
}
}
28 changes: 28 additions & 0 deletions src/main/java/racingcar/util/RaceManager.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package racingcar.util;

import racingcar.model.Car;

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

public class RaceManager {
public void movingCars(List<Car> cars) {
for(var c : cars) c.move();
}

public List<String> findWinners(List<Car> cars) {
int maxLocation = 0;

for(var c : cars) {
if(maxLocation < c.getLocation()) maxLocation = c.getLocation();
}

List<String> winners = new ArrayList<>();
for(var c : cars) {
if(maxLocation == c.getLocation())
winners.add(c.getName());
}

return winners;
}
Comment on lines 13 to 19
Copy link

Choose a reason for hiding this comment

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

다른 부분도 마찬가지지만 반복문대신 스트림을 사용해보는건 어떠세요?

Copy link
Author

Choose a reason for hiding this comment

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

이런 부분은 확실히 스트림으로 해야 직관적이고 가독성도 좋을 것 같습니다
스트림이 아직 익숙하지 않아서 우선 이렇게 만들었는데,
이번 리팩토링 과제에서 좀 더 학습하여 스트림에 익숙해져 보도록 하겠습니다

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

import racingcar.model.Car;

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

public class StringUtils {
public static String[] splitByComma(String str) {
return str.split(",");
}

public static String NumToSticks(int count) {
Copy link

Choose a reason for hiding this comment

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

여기 메소드 명이 파스칼 케이스로 적용되어 있어서 카멜 케이스로 변경해주세요!

return "-".repeat(count);
}

public static List<Car> makeCarsUsingString(String str) {
String[] strs = StringUtils.splitByComma(str);
List<Car> cars = new ArrayList<>();

for (var c : strs) {
cars.add(new Car(c));
}

return cars;
}
}
21 changes: 21 additions & 0 deletions src/main/java/racingcar/view/InputView.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package racingcar.view;

import java.util.InputMismatchException;
import java.util.Scanner;

public class InputView {
private final Scanner sc = new Scanner(System.in);

public String getStringInput() {
return sc.nextLine();
}

public int getNumInput() {
try {
return sc.nextInt();
}
catch (InputMismatchException e) {
throw new IllegalArgumentException("숫자 외 다른 형태의 값을 입력할 수 없습니다.");
}
}
}
31 changes: 31 additions & 0 deletions src/main/java/racingcar/view/OutputView.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package racingcar.view;

import racingcar.util.StringUtils;
import racingcar.model.Car;

import java.util.List;

public class OutputView {
public void getCars() {
Copy link

Choose a reason for hiding this comment

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

이 메소드 이름은 자동차 리스트를 가져오는? 의미로 볼 수 있을 것 같아요.
출력문만 있다면 get~~보다는 print~~가 더 적합해 보입니다~!

Copy link
Author

Choose a reason for hiding this comment

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

맞습니다 출력에 관련된 내용이니 get 보다는 print 가 어울릴 것 같습니다 수정하겠습니다!

System.out.println("경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)\n");
}

public void getTryCount() {
System.out.println("시도할 회수는 몇회인가요?\n");
}

public void printRaceStart() {
System.out.println("\n실행 결과");
}

public void printRaceStatus(List<Car> cars) {
for(var c : cars) {
System.out.println(c.getName() + " : " + StringUtils.NumToSticks(c.getLocation()));
}
System.out.print('\n');
}

public void printRaceFinalStatus(List<String> winners) {
System.out.print("최종 우승자 : " + String.join(", ", winners));
}
}