Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
fe78ba7
feat: RacingCar 도메인 클래스 생성
baaamk Apr 5, 2025
a0ded45
feat: RacingMachine 번호 생성 기능 구현
baaamk Apr 5, 2025
29b975b
feat: RacingCars 클래스 생성
baaamk Apr 5, 2025
a0ebe7e
feat: Race 클래스 생성
baaamk Apr 5, 2025
fd71c36
feat: startRace 메서드 수정
baaamk Apr 5, 2025
8b6443f
docs: 기능 구현 리스트 추가
baaamk Apr 5, 2025
79dd22d
docs: 제목 추가
baaamk Apr 5, 2025
6dcc583
docs: 기능 수정
baaamk Apr 5, 2025
72bdccb
feat: Round 클래스 생성
baaamk Apr 6, 2025
d57b776
feat: WinningRace 클래스 생성
baaamk Apr 6, 2025
05489be
feat: WinningRace 일급 컬렉션 사용
baaamk Apr 6, 2025
b60b7f7
docs: 수정
baaamk Apr 6, 2025
bc8e2f3
feat: 코드 삭제
baaamk Apr 6, 2025
f64b6ed
feat: 상수 클래스 생성
baaamk Apr 8, 2025
41720ef
feat: 컨트롤러 의존성 관리
baaamk Apr 8, 2025
94149ed
del: 사용 안하는 컨트롤러 삭제
baaamk Apr 8, 2025
dabf12d
del: 사용 안하는 도메인 삭제
baaamk Apr 8, 2025
3470ab6
feat: DTO 생성
baaamk Apr 8, 2025
ec9dcbc
feat: View 생성
baaamk Apr 8, 2025
b37b905
refactor: RacingCars 에 있던 책임 Race로 보냄.
baaamk Apr 8, 2025
529e242
feat: 숫자 검증 책임 위해 객체 생성
baaamk Apr 8, 2025
f881f8f
docs: 기능 수정
baaamk Apr 8, 2025
209935b
refactor: 상수 수정
baaamk Apr 8, 2025
53b9be5
refactor: import 관리
baaamk Apr 8, 2025
389d8fa
refactor: inline으로 수정
baaamk Apr 8, 2025
e52178d
refactor: 매직넘버 수정
baaamk Apr 8, 2025
d0c80b0
refactor: import 수정
baaamk Apr 8, 2025
89b9cab
refactor: 리스트 수정 못하도록 읽기 전용으로 변경
baaamk Apr 8, 2025
f2a6a3b
refactor: 리스트 수정 못하도록 읽기 전용으로 변경
baaamk Apr 8, 2025
f91d958
refactor: validate 위치 변경
baaamk Apr 9, 2025
9317cc2
refactor: 상수 클래스 삭제
baaamk Apr 9, 2025
e0ed130
refactor: 불변 리스트로 초기화
baaamk Apr 9, 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
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ dependencies {

java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
languageVersion = JavaLanguageVersion.of(17)
}
}

Expand Down
25 changes: 25 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
## 도메인
### 모델
- [X] RacingCar 클래스
- [X] name 길이 검증 메서드
- [X] name 존재 검증 메서드
- [X] name 공백 검증 메서드
- [X] position move 메서드
- [X] RacingCar을 리스트로 한 RacingCars 클래스
- [X] 이름 중복 검증 메서드
- [X] 이름 목록 Empty 검증 메서드
- [X] 레이스 참가 레이싱카 생성 팩토리 메서드 //이펙티브 자바
- [X] 게임을 담당할 Race 클래스 생성
- [X] 게임 시작 메서드
- [X] position 비교 메서드
- [X] 이곳에서 경기 결과 나오는게 맞다 판단하여 findResult, getWinner 메서드 생성
- [X] 게임 시도 숫자 검증할 TryNumber 클래스
Comment on lines +3 to +16

Choose a reason for hiding this comment

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

객체간 의존성을 그림으로 그리면 어떤 관계를 가지고 있는지 쉽게 알 수 있을 것 같아요



## 컨트롤러
- [X] 컨트롤러(컨트롤러 인터페이스화)
-
## View
- [X] view(인터페이스로 나누기 outView 재사용 위해)
- [X] 출력 되는 것들은 전부 outView 로
- [X] 입력받는 것들은 전부 inputView 로
22 changes: 22 additions & 0 deletions src/main/java/racingcar/AppConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package racingcar;

import racingcar.controller.Controller;
import racingcar.controller.RaceController;
import racingcar.view.InputView;
import racingcar.view.OutputView;

public class AppConfig {

public Controller controller() {
return new RaceController(inputView(),outputView());

Choose a reason for hiding this comment

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

Suggested change
return new RaceController(inputView(),outputView());
return new RaceController(inputView(), outputView());

컨벤션!

}

private InputView inputView() {
return new InputView();
}
private OutputView outputView() {
return new OutputView();
}

Choose a reason for hiding this comment

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

Suggested change

필요없는 줄바꿈 제거해주세요


}
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.Controller;

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

Controller controller = appConfig.controller();

controller.execute();
}
}
5 changes: 5 additions & 0 deletions src/main/java/racingcar/controller/Controller.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package racingcar.controller;

public interface Controller {
void execute();
}
73 changes: 73 additions & 0 deletions src/main/java/racingcar/controller/RaceController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package racingcar.controller;

import racingcar.domain.model.*;
import racingcar.domain.model.dto.FinalWinnerDTO;
import racingcar.domain.model.dto.RoundResultDTO;
import racingcar.global.ErrorMessage;
import racingcar.view.InputView;
import racingcar.view.OutputView;

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

public class RaceController implements Controller {
public static final int START_ROUND = 1;

private final InputView inputView;
private final OutputView outputView;

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

@Override
public void execute() {
RacingCars racingCars = inputName();

Choose a reason for hiding this comment

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

메서드명을 적절히 수정해주면 좋을 것 같아요!
inputName이라는 행위에 RacingCars를 생성하고 반환하는 것은 적절하지 않은 것 같아요!

Race race = raceProcess(racingCars);
printResult(race);
}

private void printResult(Race race) {
String finalResult = FinalWinnerDTO.from(race).getWinnersAsString();
outputView.printWinningResult(finalResult);
}

private Race raceProcess(RacingCars racingCars) {
outputView.printTryNumberNotice();
TryNumber tryNumber = validateInputTryNumber(inputView.inputTryNumber());
Comment on lines +38 to +39

Choose a reason for hiding this comment

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

입력을 위한 출력 메시지는 입력 객체 내부에서 진행하는 것은 어떤가요?
두 방법에 대해 장단점이 있을 것 같아요~

Comment on lines +38 to +39

Choose a reason for hiding this comment

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

시도 횟수이므로

Suggested change
outputView.printTryNumberNotice();
TryNumber tryNumber = validateInputTryNumber(inputView.inputTryNumber());
outputView.printTryCountNotice();
TryCount tryCount = validateInputTryCount(inputView.inputTryCount());

이 네이밍은 어떤가요?

Race race = null;
for (int i = START_ROUND; i <= tryNumber.getTryNumber(); i++) {
race = Race.create(racingCars, tryNumber);
outputView.printRoundResult(RoundResultDTO.from(race.startRace(),race));
}
return race;
}
Comment on lines +40 to +46

Choose a reason for hiding this comment

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

출력로직과 도메인 로직이 얽혀있는 것 같아요!
만약 모든 Race를 활용하여 다른 요구사항을 추가하고 싶을 때, 유연하지 않을 것 같다고 생각합니다.

Race를 끝마치고 출력하는 방식으로 바꿔보는 것은 어떤가요?


private RacingCars inputName() {
outputView.printInitialName();
return RacingCars.create(createRacingCars());
}


private List<RacingCar> createRacingCars() {
return Arrays.stream(inputView.inputName().split(","))
.map(String::trim)
.map(RacingCar::create)
.collect(Collectors.toList());

Choose a reason for hiding this comment

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

java 16버전에 새로 나온 .toList()를 사용하지 않으신 이유가 있나요?

}
Comment on lines +54 to +59

Choose a reason for hiding this comment

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

CarName 컬렉션을 만드는 행위와
CarName들로 RacingCar인스턴스를 생성하는 행위를 분리하면 가독성이 더 좋아질 것 같아요



private TryNumber validateInputTryNumber(String tryNumber) {
try {
if (tryNumber.trim().isEmpty()) {
throw new IllegalArgumentException(ErrorMessage.EMPTY_INPUT);
}
return TryNumber.from(Integer.parseInt(tryNumber));
} catch (NumberFormatException e) {
throw new IllegalArgumentException(ErrorMessage.INVALID_NUMBER_FORMAT);
}

}
}
50 changes: 50 additions & 0 deletions src/main/java/racingcar/domain/model/Race.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package racingcar.domain.model;

import racingcar.global.ErrorMessage;

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

public class Race {

private final RacingCars racingCars;
private final TryNumber tryNumber;
Comment on lines +9 to +12

Choose a reason for hiding this comment

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

시도 횟수마다 인스턴스가 생성되는데, 전체적으로 관리되는 Race라는 네이밍은 조금 추상적인 것 같아요!
RaceHistory와 같은 이름은 어떤가요? 주관적인 피드백입니다~


private Race(RacingCars racingCars, TryNumber tryNumber) {
this.racingCars = racingCars;
this.tryNumber = tryNumber;
}

public static Race create(RacingCars racingCars, TryNumber tryNumber) {
return new Race(racingCars, tryNumber);
}

public RacingCars startRace() {
return RacingCars.create(racingCars.moveAll());

Choose a reason for hiding this comment

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

moveAll에서 새로운 RacingCars를 생성한 뒤 반환하는 것은 어떤가요?

}
Comment on lines +23 to +25

Choose a reason for hiding this comment

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

시작하는 것뿐만 아니라 계속해서 시도하고 있는 것이라면 try가 낫지 않을까요?

Suggested change
public RacingCars startRace() {
return RacingCars.create(racingCars.moveAll());
}
public RacingCars tryRace() {
return RacingCars.create(racingCars.moveAll());
}


public Map<String, Integer> findResult() {

Choose a reason for hiding this comment

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

일급컬렉션을 생성하여 반환하면 의미 전달이 더 잘될 것 같아요

return racingCars.getRacingCars().stream()
.collect(Collectors.toMap(
RacingCar::getName,
RacingCar::getPosition
));
}

public List<String> getWinners() {

Choose a reason for hiding this comment

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

일급컬렉션으로 변경해도 좋을 것 같아요

int winningPosition = findWinningPosition();
return racingCars.getRacingCars().stream()
.filter(car -> car.getPosition() == winningPosition)

Choose a reason for hiding this comment

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

car 객체에게 메시지를 던질 수 있을 것 같아요

.map(RacingCar::getName)
.toList();
}

private int findWinningPosition() {
return racingCars.getRacingCars().stream()
.mapToInt(RacingCar::getPosition)
.max()
.orElseThrow(() -> new IllegalStateException(ErrorMessage.NO_CARS_TO_RACE));
}
}

52 changes: 52 additions & 0 deletions src/main/java/racingcar/domain/model/RacingCar.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package racingcar.domain.model;

import racingcar.global.ErrorMessage;

import static camp.nextstep.edu.missionutils.Randoms.pickNumberInRange;

public class RacingCar {
public static final int MIN = 0;
public static final int MAX = 9;
public static final int MOVE_NUMBER = 4;
public static int START_POSITION = 0;
Comment on lines +8 to +11

Choose a reason for hiding this comment

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

public으로 선언하신 이유가 있나요?


private final String name;
private int position = START_POSITION;

Choose a reason for hiding this comment

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

추가된 요구사항에 외부에서 포지션을 지정해주고 싶다면 변경이 힘들것 같아요.
하지만 거의 변경되지 않을 요구사항이라면 바꾸지 않아도 괜찮을 것 같아요~


private RacingCar(String name) {
validateNameLength(name);
validateNameEmpty(name);
Comment on lines +16 to +18

Choose a reason for hiding this comment

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

Name과 관련된 검증이 이루어지고 있는데, CarName 객체를 새로 생성하는 것은 어떨까요?

this.name = name;
}

public static RacingCar create(String name) {
return new RacingCar(name);
}

private void validateNameLength(String name) {
if (name.length() > 5) {
throw new IllegalArgumentException(ErrorMessage.NAME_TOO_LONG);
}
}

private void validateNameEmpty(String name) {
if (name == null) {
throw new IllegalArgumentException(ErrorMessage.NAME_MISSING);
}
}

public void move() {
if (pickNumberInRange(MIN, MAX) >= MOVE_NUMBER) {
position++;
}
}

public String getName() {
return name;
}


public int getPosition() {
return position;
}
}
42 changes: 42 additions & 0 deletions src/main/java/racingcar/domain/model/RacingCars.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package racingcar.domain.model;

import racingcar.global.ErrorMessage;

import java.util.List;

public class RacingCars {
private final List<RacingCar> racingCars;

private RacingCars(List<RacingCar> racingCars) {
validateNameListEmpty(racingCars);
validateDuplicateName(racingCars);
this.racingCars = List.copyOf(racingCars);
}

public static RacingCars create(List<RacingCar> userInput) {
return new RacingCars(userInput);
}

public List<RacingCar> moveAll() {

Choose a reason for hiding this comment

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

컬렉션을 그대로 반환한다면 일급 컬렉션을 생성한 의미가 떨어지지 않을까요?

racingCars.forEach(RacingCar::move);
return racingCars;
}


private void validateDuplicateName(List<RacingCar> racingCars) {
if (racingCars.size() != racingCars.stream().distinct().count()) {

Choose a reason for hiding this comment

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

이름이 같다고 같은 객체임을 구현해주지 않았는데, 제대로 동작하나요!?

throw new IllegalArgumentException(ErrorMessage.DUPLICATE_NAME);
}
}

private void validateNameListEmpty(List<RacingCar> racingCars) {
if (racingCars.isEmpty()) {
throw new IllegalArgumentException(ErrorMessage.EMPTY_NAME_LIST);
}
}


public List<RacingCar> getRacingCars() {
return racingCars;
}
}
29 changes: 29 additions & 0 deletions src/main/java/racingcar/domain/model/TryNumber.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package racingcar.domain.model;

import racingcar.global.ErrorMessage;

public class TryNumber {
public static final int MIN = 0;


private final int tryNumber;
Comment on lines +6 to +9

Choose a reason for hiding this comment

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

Suggested change
public static final int MIN = 0;
private final int tryNumber;
public static final int MIN = 0;
private final int tryNumber;

줄바꿈도 컨벤션!


private TryNumber(int tryNumber) {
validateTryNumber(tryNumber);
this.tryNumber = tryNumber;
}

public static TryNumber from(int tryNumber) {
return new TryNumber(tryNumber);
}
Comment on lines +16 to +18

Choose a reason for hiding this comment

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

다른 객체는 create인데, 이 객체는 from이네요!
통일해주면 사용하기 편할 것 같아요~


private void validateTryNumber(int tryNumber) {
if (tryNumber <= MIN) {
throw new IllegalArgumentException(ErrorMessage.INVALID_TRY_NUMBER);
}
}

public int getTryNumber() {
return tryNumber;
}
}
22 changes: 22 additions & 0 deletions src/main/java/racingcar/domain/model/dto/FinalWinnerDTO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package racingcar.domain.model.dto;

import racingcar.domain.model.Race;


public class FinalWinnerDTO {
private final Race race;

private FinalWinnerDTO(Race race) {
this.race = race;
}

public static FinalWinnerDTO from(Race race) {
return new FinalWinnerDTO(race);
}

public String getWinnersAsString() {
return String.join(", ", race.getWinners());
}


}
26 changes: 26 additions & 0 deletions src/main/java/racingcar/domain/model/dto/RoundResultDTO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package racingcar.domain.model.dto;

import racingcar.domain.model.Race;
import racingcar.domain.model.RacingCars;

import java.util.Map;

public class RoundResultDTO {
private final RacingCars racingCars;
private final Race race;

private RoundResultDTO(RacingCars racingCars, Race race) {
this.racingCars = racingCars;
this.race = race;
}


public static RoundResultDTO from(RacingCars racingCars, Race race) {
return new RoundResultDTO(racingCars, race);
}


public Map<String,Integer> getRacingCars() {
return race.findResult();
}
}
14 changes: 14 additions & 0 deletions src/main/java/racingcar/global/ErrorMessage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package racingcar.global;

public class ErrorMessage {
public static final String EMPTY_INPUT = "빈 값이 들어오면 안됩니다.";
public static final String INVALID_NUMBER_FORMAT = "정수만 입력 가능합니다.";

public static final String NAME_TOO_LONG = "이름은 5자 이하여야 합니다.";
public static final String NAME_MISSING = "이름이 존재하지 않습니다.";
public static final String DUPLICATE_NAME = "중복된 이름이 존재합니다.";
public static final String EMPTY_NAME_LIST = "이름 목록이 비어있습니다.";

public static final String NO_CARS_TO_RACE = "경주할 자동차가 없습니다.";
public static final String INVALID_TRY_NUMBER = "시도 숫자는 0보다 커야합니다.";
}
Loading