-
Notifications
You must be signed in to change notification settings - Fork 452
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
[2단계 - 자동차 경주 리팩토링] 검프(김태정) 미션 제출합니다. #217
Changes from all commits
19d9f6f
3a3c1e3
880f835
8cafb73
e2980c4
4bfd56e
919443e
7e74c4f
778c305
86aca54
c4f2b1f
dd2a453
d66a7e7
2a42110
071620f
cd9d349
08c318f
bc3613d
54caae2
7d30beb
426921d
22370bb
c0a24aa
5560c58
2608a07
15fe424
782086b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,29 +1,45 @@ | ||
## 객체 협력 정리 | ||
|
||
 | ||
## 객체 메시지 정리 | ||
|
||
## 객체 메시지 정리 | ||
|
||
- 게임 시작하라 | ||
- 경주를 진행하라 | ||
- 랜덤값을 반환해라 | ||
- 자동차 움직여라 | ||
- 입력하라 | ||
- 자동차 움직여라 | ||
- 입력하라 | ||
- 메뉴를 출력하라 | ||
- 자동차 위치를 반환하라 | ||
- 자동차 위치를 출력하라 | ||
- 자동차 위치를 출력하라 | ||
- 자동차 모음을 생성하라 | ||
- 우승자를 출력하라 | ||
- 우승자를 출력하라 | ||
|
||
## 구현할 기능 목록 정리 | ||
|
||
- [x] 객체 모델링 | ||
- [x] 쉼표를 기준으로 구분된 n(최소 2 이상)대의 보장된 자동차 이름을 입력받는다. | ||
- [x] 보장된 자동차 이름(빈 문자열이 아닌 5자 이하의 영어로만 구성). | ||
- [x] 쉼표가 아닌 다른 구분자가 들어오면 에러가 발생한다. | ||
- [x] 보장된 자동차 이름(빈 문자열이 아닌 5자 이하의 영어로만 구성). | ||
- [x] 쉼표가 아닌 다른 구분자가 들어오면 에러가 발생한다. | ||
- [x] 최소 1회 이상의 이동 횟수를 입력받는다. | ||
- [x] 음수, 실수, 문자열 등 유효하지 않은 값을 입력받으면 에러가 발생한다. | ||
- [x] 음수, 실수, 문자열 등 유효하지 않은 값을 입력받으면 에러가 발생한다. | ||
- [x] 게임이 한번 실행될때 tryCount가 줄어든다. | ||
- [x] 각 자동차들은 이동 횟수만큼 전진할 수 있는 기회를 부여받는다. | ||
- [x] 랜덥값을 생성한다 | ||
- [x] 전진 조건은 0에서 9 사이의 random 값을 구한 후 random 값이 4 이상인 경우이다. | ||
- [x] 각 회차별 실행 결과를 출력한다. | ||
- [x] 게임이 끝나면 우승자가 나온다. | ||
- [x] 랜덥값을 생성한다 | ||
- [x] 전진 조건은 0에서 9 사이의 random 값을 구한 후 random 값이 4 이상인 경우이다. | ||
- [x] 각 회차별 실행 결과를 출력한다. | ||
- [x] 게임이 끝나면 우승자가 나온다. | ||
- [x] 가장 멀리 이동한 1명 이상의 우승자 명단을 탐색해 출력한다. | ||
|
||
## 리펙토링 목록 정리 | ||
|
||
- [x] Screen 추상클래스 인터페이스로 변경 | ||
- [x] Dto를 어떻게 반환할 것인지, dto의 패키지 또한 변경한다. (Cars, Winners 객체) | ||
- [x] 테스트를 위한 메소드를 삭제한다. | ||
- [x] Collections.singletonList()를 사용한다. | ||
- [x] 테스트 클래스에서 @DisplayName으로 테스트의 용도를 설명한다 | ||
- [x] 에러메세지 출력시 사용자가 어떤 값을 입력햇는지 또한 출력한다. | ||
- [x] `할당 -> 검증`의 과정을 `복사(방어) -> 검증 -> 할당`의 과정으로 변환 | ||
- [x] 매개변수르 들어오는 값들을 final 처리 | ||
- [x] 원시값을 객체로 포장한다. | ||
- [x] 중복 이름에 대해서 검증한다 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,47 +1,41 @@ | ||
package racing.domain; | ||
|
||
import java.util.Objects; | ||
import java.util.regex.Matcher; | ||
import java.util.regex.Pattern; | ||
import racing.domain.name.CarName; | ||
import racing.domain.number.Position; | ||
|
||
public class Car { | ||
private static final Pattern PATTERN = Pattern.compile("[a-zA-Z]{1,5}"); | ||
private static final int MINIMUM_MOVE_NUMBER = 4; | ||
private static final int DEFAULT_POSITION = 0; | ||
|
||
private final String name; | ||
private int position; | ||
private final CarName name; | ||
private final Position position; | ||
|
||
public Car(String name) { | ||
public Car(final CarName name) { | ||
this.name = name; | ||
this.position = DEFAULT_POSITION; | ||
validateName(); | ||
this.position = Position.of(); | ||
} | ||
|
||
private void validateName() { | ||
if (Objects.isNull(this.name)) { | ||
throw new IllegalArgumentException(); | ||
} | ||
Matcher matcher = PATTERN.matcher(this.name); | ||
if (!matcher.matches()) { | ||
throw new IllegalArgumentException(); | ||
} | ||
} | ||
|
||
public boolean move(int randomNumber) { | ||
public boolean move(final int randomNumber) { | ||
if (randomNumber >= MINIMUM_MOVE_NUMBER) { | ||
this.position++; | ||
position.add(); | ||
return true; | ||
} | ||
return false; | ||
} | ||
|
||
public int getPosition() { | ||
public int getPositionValue() { | ||
return position.getValue(); | ||
} | ||
|
||
public Position getPosition() { | ||
return position; | ||
} | ||
|
||
public boolean isSamePosition(Position maxPosition) { | ||
return position.equals(maxPosition); | ||
} | ||
|
||
public String getName() { | ||
return name; | ||
return name.getName(); | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,68 +1,78 @@ | ||
package racing.domain; | ||
|
||
import racing.domain.dto.CarDto; | ||
import racing.domain.name.CarName; | ||
import racing.domain.name.CarNames; | ||
import racing.domain.number.Position; | ||
import racing.utils.RandomUtils; | ||
|
||
import java.util.ArrayList; | ||
import java.util.Arrays; | ||
import java.util.Collections; | ||
import java.util.Comparator; | ||
import java.util.List; | ||
import java.util.stream.Collectors; | ||
|
||
public class Cars { | ||
private static final String DELIMITER = ","; | ||
private static final int SPLIT_THRESHOLD = -1; | ||
public class Cars implements Cloneable { | ||
private static final int START_NUMBER = 0; | ||
private static final int END_NUMBER = 9; | ||
private static final int MINIMUM_CAR_COUNTS = 2; | ||
private static final int MINIMUM_CAR_SIZE = 2; | ||
|
||
private final List<Car> cars; | ||
|
||
private Cars(List<Car> cars) { | ||
this.cars = new ArrayList<>(cars); | ||
validateCars(); | ||
private Cars(final List<Car> cars) { | ||
List<Car> copy = new ArrayList<>(cars); | ||
validateCars(copy); | ||
this.cars = copy; | ||
} | ||
|
||
public static Cars generate(String carNames) { | ||
String[] splitCarNames = splitCarNames(carNames); | ||
List<Car> cars = Arrays.stream(splitCarNames) | ||
.map(Car::new) | ||
public static Cars generate(final CarNames inputCarNames) { | ||
List<CarName> carNames = inputCarNames.getCarNames(); | ||
List<Car> cars = carNames.stream() | ||
.map(carName -> new Car(carName)) | ||
.collect(Collectors.toList()); | ||
return new Cars(cars); | ||
} | ||
|
||
private static String[] splitCarNames(String carNames) { | ||
return carNames.split(DELIMITER, SPLIT_THRESHOLD); | ||
private void validateCars(List<Car> cars) { | ||
if (cars.size() < MINIMUM_CAR_SIZE) { | ||
throw new IllegalArgumentException("자동차는 1개 이상이어야 합니다"); | ||
} | ||
} | ||
|
||
private void validateCars() { | ||
if (cars.size() < MINIMUM_CAR_COUNTS) { | ||
throw new IllegalArgumentException(); | ||
@Override | ||
protected Cars clone() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이부분은 질문을 하셨기에 남깁니다! 그냥 참고로만 말씀드리면 객체를 생성자를 통해서 만드는것이 아니고 필드를 복사하며 만들기 때문에 불변을 보장하기가 어렵습니다. 이것으로 인한 버그들이 많다고알고있는데요, shallow copy를 통한 작업으로 인해 정말 의도한 대로 복사가 이루어지지 않을 수 있습니다. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
Cars copy = null; | ||
try { | ||
copy = (Cars) super.clone(); | ||
} catch (CloneNotSupportedException e) { | ||
e.printStackTrace(); | ||
} | ||
return copy; | ||
} | ||
|
||
public void race() { | ||
cars.forEach(car -> car.move(RandomUtils.getRandomNumber(START_NUMBER, END_NUMBER))); | ||
} | ||
|
||
public Winners findWinners() { | ||
int maxPosition = getMaxPosition(); | ||
Position maxPosition = getMaxPosition(); | ||
List<Car> winners = cars.stream() | ||
.filter(car -> car.getPosition() == maxPosition) | ||
.filter(car -> car.isSamePosition(maxPosition)) | ||
.collect(Collectors.toList()); | ||
return new Winners(winners); | ||
} | ||
|
||
private int getMaxPosition() { | ||
private Position getMaxPosition() { | ||
return cars.stream() | ||
.max(Comparator.comparingInt(Car::getPosition)) | ||
.max(Comparator.comparing(Car::getPosition)) | ||
.orElseThrow(IllegalStateException::new) | ||
.getPosition(); | ||
} | ||
|
||
public List<CarDto> getCarDtos() { | ||
return cars.stream() | ||
.map(CarDto::of) | ||
.collect(Collectors.toList()); | ||
public List<Car> getCars() { | ||
return Collections.unmodifiableList(cars); | ||
} | ||
|
||
public int getSize() { | ||
return cars.size(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -1,44 +1,47 @@ | ||||||
package racing.domain; | ||||||
|
||||||
import racing.domain.dto.CarDto; | ||||||
import racing.domain.number.TryCount; | ||||||
import racing.view.GameScreen; | ||||||
import racing.view.dto.CarMoveStatusDto; | ||||||
import racing.view.dto.WinnersStatusDto; | ||||||
|
||||||
import java.util.List; | ||||||
import java.util.stream.Collectors; | ||||||
|
||||||
public class RacingGameMachine { | ||||||
private static final int ZERO = 0; | ||||||
private static final int MINIMUM_CAR_COUNTS = 2; | ||||||
|
||||||
private final Cars cars; | ||||||
private int tryCounts; | ||||||
private final TryCount tryCount; | ||||||
|
||||||
public RacingGameMachine(Cars cars, int tryCounts) { | ||||||
this.cars = cars; | ||||||
this.tryCounts = tryCounts; | ||||||
validateTryCounts(); | ||||||
public RacingGameMachine(final Cars cars, final TryCount tryCount) { | ||||||
Cars copy = cars.clone(); | ||||||
validateCars(copy); | ||||||
this.cars = copy; | ||||||
this.tryCount = tryCount; | ||||||
} | ||||||
|
||||||
private void validateTryCounts() { | ||||||
if (tryCounts <= ZERO) { | ||||||
throw new IllegalArgumentException(); | ||||||
private void validateCars(final Cars cars) { | ||||||
if (cars.getSize() < MINIMUM_CAR_COUNTS) { | ||||||
throw new IllegalArgumentException("자동차 이름은 1개 이상이어야 합니다"); | ||||||
} | ||||||
} | ||||||
|
||||||
public void play() { | ||||||
GameScreen gameScreen = new GameScreen(); | ||||||
gameScreen.showMessage(); | ||||||
while (tryCounts-- > ZERO) { | ||||||
while (tryCount.reduce()) { | ||||||
cars.race(); | ||||||
gameScreen.showCarStatus(getCarDtos()); | ||||||
} | ||||||
Winners winners = cars.findWinners(); | ||||||
gameScreen.showWinners(winners.getWinnersDto()); | ||||||
} | ||||||
|
||||||
public boolean canPlay() { | ||||||
return tryCounts > ZERO; | ||||||
Winners winners = cars.findWinners(); | ||||||
gameScreen.showWinners(new WinnersStatusDto(winners.getWinnersName())); | ||||||
} | ||||||
|
||||||
private List<CarDto> getCarDtos() { | ||||||
return cars.getCarDtos(); | ||||||
private List<CarMoveStatusDto> getCarDtos() { | ||||||
return cars.getCars().stream() | ||||||
.map(car -> CarMoveStatusDto.of(car)) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
🙏 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 확인했습니다! 감사합니다. |
||||||
.collect(Collectors.toList()); | ||||||
} | ||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,29 +1,27 @@ | ||
package racing.domain; | ||
|
||
import racing.domain.dto.WinnersDto; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.stream.Collectors; | ||
|
||
public class Winners { | ||
private final List<Car> cars; | ||
|
||
public Winners(List<Car> cars) { | ||
this.cars = new ArrayList<>(cars); | ||
validateWinners(); | ||
public Winners(final List<Car> cars) { | ||
List<Car> copy = new ArrayList<>(cars); | ||
validateWinners(copy); | ||
this.cars = copy; | ||
} | ||
|
||
public WinnersDto getWinnersDto() { | ||
List<String> winnersName = cars.stream() | ||
public List<String> getWinnersName() { | ||
return cars.stream() | ||
.map(Car::getName) | ||
.collect(Collectors.toList()); | ||
return new WinnersDto(winnersName); | ||
} | ||
|
||
private void validateWinners() { | ||
if (this.cars.isEmpty()) { | ||
throw new IllegalStateException(); | ||
private void validateWinners(final List<Car> copy) { | ||
if (copy.isEmpty()) { | ||
throw new IllegalStateException("우승자는 1명 이상이어야 합니다"); | ||
} | ||
} | ||
} |
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
피드백에 관련한 리팩토링 목록도 정리하셨네요 👍
멋집니다!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
문서화를 습관화 하겠습니다. 😊