-
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
[스티치] 자동차 경주 게임 미션 코드리뷰 제출합니다. #93
Changes from all commits
a842b20
c4c8e5f
d80f88c
79a5933
b70ee54
271b121
5430ccb
84a6723
f96307b
e0d1884
c71fdeb
24f3337
be57040
4a980d2
8082849
f468499
991ee17
88058b6
3211834
d0d6b27
11d1fd3
e9e5e46
e4719be
8b88110
8735792
f7fe364
5b51208
6818977
6d462c9
b93e95e
6f3992a
44d7ae1
c52c332
b25d3ec
44dc6c3
41e5da9
740b5b8
6509621
80cc9b8
a2d1ad1
5a83559
44c705d
23b32a4
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,5 +1,68 @@ | ||
# java-racingcar | ||
|
||
자동차 경주 게임 미션 저장소 | ||
|
||
## 기능 요구사항 | ||
|
||
- 주어진 횟수 동안 n대의 자동차는 전진 또는 멈출 수 있다. | ||
|
||
- 각 자동차에 이름을 부여할 수 있다. 전진하는 자동차를 출력할 때 자동차 이름을 같이 출력한다. | ||
|
||
- 자동차 이름은 쉼표(,)를 기준으로 구분하며 이름은 5자 이하만 가능하다. | ||
|
||
- 사용자는 몇 번의 이동을 할 것인지를 입력할 수 있어야 한다. | ||
|
||
- 전진하는 조건은 0에서 9 사이에서 random 값을 구한 후 random 값이 4 이상일 경우 전진하고, 3 이하의 값이면 - | ||
멈춘다. | ||
|
||
- 자동차 경주 게임을 완료한 후 누가 우승했는지를 알려준다. 우승자는 한 명 이상일 수 있다. | ||
|
||
## 기능 구현 목록 | ||
|
||
- 자동차의 이름을 한 줄로 입력받는 기능 | ||
|
||
- 입력받은 자동차의 이름을 쉼표(,)로 나누는 기능 | ||
|
||
- 이름이 5자를 넘지 않는지 확인하는 기능 | ||
|
||
- 중복된 이름이 존재하는지 여부를 확인하는 기능 | ||
|
||
- 몇 번의 이동을 할 것인지를 입력받는 기능 | ||
|
||
- 이동 횟수 입력에 대한 유효성을 검증하는 기능 | ||
|
||
- 전진하는 조건을 생성하는 기능 | ||
|
||
- 0에서 9까지의 random 값을 생성하는 기능 | ||
|
||
- random 값이 4 이상일 경우 전진하는 기능 | ||
|
||
- random 값이 3 이하인 경우 멈추는 기능 | ||
|
||
- 주어진 횟수에 따라 자동차를 전진하는 기능 | ||
|
||
- 자동차가 전진하는 경우 위치를 1 증가시키는 기능 | ||
|
||
- 여러대의 자동차를 관리하는 기능 | ||
|
||
- 자동차들을 이동시키는 기능 | ||
|
||
- 매번 이동이 끝난 후 자동차의 이름과 이동상황을 출력하는 기능 | ||
|
||
- 우승자를 출력하는 기능 | ||
|
||
- 자동차가 우승한 위치에 있는지 확인하는 기능 | ||
|
||
- 우승한 자동차(들)을 반환해주는 기능 | ||
|
||
- 우승한 자동차(들)을 출력하는 기능 | ||
|
||
## 기여자 | ||
|
||
- [라흐]() | ||
|
||
- [스티치]() | ||
|
||
## 우아한테크코스 코드리뷰 | ||
* [온라인 코드 리뷰 과정](https://github.com/woowacourse/woowacourse-docs/blob/master/maincourse/README.md) | ||
|
||
- [온라인 코드 리뷰 과정](https://github.com/woowacourse/woowacourse-docs/blob/master/maincourse/README.md) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
package com.woowacourse.racingGame; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
import com.woowacourse.racingGame.domain.Cars; | ||
import com.woowacourse.racingGame.domain.CarsFactory; | ||
import com.woowacourse.racingGame.domain.MovementNumber; | ||
import com.woowacourse.racingGame.domain.RandomMovableStrategy; | ||
import com.woowacourse.racingGame.domain.Result; | ||
import com.woowacourse.racingGame.view.InputView; | ||
import com.woowacourse.racingGame.view.OutputView; | ||
|
||
public class Application { | ||
private static InputView inputview; | ||
private static OutputView outputView; | ||
|
||
public static void main(String[] args) { | ||
inputview = new InputView(); | ||
outputView = new OutputView(); | ||
|
||
final Cars cars = generateCars(); | ||
final MovementNumber movementNumber = receiveInputMovementNumber(); | ||
|
||
// NOTE : Log처럼 기록을 출력하는 것이 맞는지 여부 물어보기. | ||
final List<Result> results = playRacingGame(cars, movementNumber); | ||
|
||
outputView.printRacingGameResult(results); | ||
outputView.printWinners(getFinalResult(results, movementNumber)); | ||
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. 위의 부분에서 매번 경주를 한번 진행할 때마다(자동차 경기 Lap마다) 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. 이 부분은 결과 데이터를 가지고 어떤 기능을 추가적으로 만들 것 인가에 따라 달라질 것 같아요. |
||
} | ||
|
||
private static Cars generateCars() { | ||
try { | ||
final String carName = inputview.inputCarName(); | ||
return CarsFactory.generate(carName, new RandomMovableStrategy()); | ||
} catch (IllegalArgumentException e) { | ||
outputView.printExceptionMessage(e.getMessage()); | ||
return generateCars(); | ||
} | ||
} | ||
|
||
private static MovementNumber receiveInputMovementNumber() { | ||
try { | ||
final String movementNumber = inputview.inputMovementNumber(); | ||
return new MovementNumber(movementNumber); | ||
} catch (IllegalArgumentException e) { | ||
outputView.printExceptionMessage(e.getMessage()); | ||
return receiveInputMovementNumber(); | ||
} | ||
} | ||
|
||
private static List<Result> playRacingGame(final Cars cars, final MovementNumber movementNumber) { | ||
List<Result> results = new ArrayList<>(); | ||
|
||
for (int i = 0; i < movementNumber.getMovementNumber(); i++) { | ||
attemptMove(cars); | ||
results.add(new Result(cars)); | ||
} | ||
return results; | ||
} | ||
|
||
private static void attemptMove(final Cars cars) { | ||
cars.attemptMove(); | ||
} | ||
|
||
private static Result getFinalResult(final List<Result> results, final MovementNumber movementNumber) { | ||
return results.get(movementNumber.getMovementNumber() - 1); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
package com.woowacourse.racingGame.domain; | ||
|
||
import java.util.Objects; | ||
|
||
public class Car { | ||
private final MovableStrategy movableStrategy; | ||
private final Name name; | ||
private Position position; | ||
|
||
public Car(Name name, MovableStrategy movableStrategy) { | ||
this.position = Position.ZERO; | ||
this.name = name; | ||
this.movableStrategy = movableStrategy; | ||
} | ||
|
||
Car(Name name, Position position, MovableStrategy movableStrategy) { | ||
this.position = position; | ||
this.name = name; | ||
this.movableStrategy = movableStrategy; | ||
} | ||
|
||
public void attemptMoveThrough() { | ||
if (movableStrategy.isMovable()) { | ||
position = position.increaseByMovingUnit(); | ||
} | ||
} | ||
|
||
// NOTE : 불변객체의 이점? | ||
public boolean isSamePosition(final int winnerPosition) { | ||
return position.getPosition() == winnerPosition; | ||
} | ||
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. 데이브님께서 캐싱을 활용해보라고 했던 그런데 이번 미션에서 보면 이동이 자주 발생할 수 있기 때문에 차라리 제가 구현한 것 처럼 인스턴스 변수가 변하지 않도록 구현하는 것이 이번 미션에서 효율적이지 않은 방법일까요? 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. 이 글을 읽어보세요. |
||
|
||
public String getName() { | ||
return name.getName(); | ||
} | ||
|
||
public int getPosition() { | ||
return position.getPosition(); | ||
} | ||
|
||
@Override | ||
public boolean equals(Object o) { | ||
if (this == o) | ||
return true; | ||
if (o == null || getClass() != o.getClass()) | ||
return false; | ||
Car car = (Car)o; | ||
return name.equals(car.name); | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return Objects.hash(name); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
package com.woowacourse.racingGame.domain; | ||
|
||
import java.util.Arrays; | ||
import java.util.Collections; | ||
import java.util.List; | ||
import java.util.Objects; | ||
|
||
public class Cars { | ||
private final List<Car> cars; | ||
|
||
public Cars(final List<Car> cars) { | ||
checkDuplicate(cars); | ||
this.cars = cars; | ||
} | ||
|
||
private void checkDuplicate(final List<Car> cars) { | ||
if (isDuplicateExist(cars)) { | ||
throw new IllegalArgumentException("중복된 이름이 존재합니다."); | ||
} | ||
} | ||
|
||
private boolean isDuplicateExist(final List<Car> cars) { | ||
long distinctCarSize = Arrays.stream(cars.toArray()) | ||
.distinct() | ||
.count(); | ||
return cars.size() != distinctCarSize; | ||
} | ||
|
||
public void attemptMove() { | ||
for (Car car : cars) { | ||
car.attemptMoveThrough(); | ||
} | ||
} | ||
|
||
// NOTE : unmodifiable로 처리해주는 것이 의미를 가지는지 리뷰어님께 여쭤보기. | ||
public List<Car> getCars() { | ||
return Collections.unmodifiableList(cars); | ||
} | ||
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. 제가 공부하기론 일급 컬렉션의 경우 최대한 외부에서 접근할 수 없도록 getter 메소드를 지양하라고 들었습니다. 여기선 출력을 위해서 어쩔 수 없이 getter를 사용하였는데 위처럼 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. getter로 받은 원소를 변경 불가능하게 하기 위해 |
||
|
||
@Override | ||
public boolean equals(Object o) { | ||
if (this == o) | ||
return true; | ||
if (o == null || getClass() != o.getClass()) | ||
return false; | ||
Cars cars = (Cars)o; | ||
return Objects.equals(this.cars, cars.cars); | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return Objects.hash(cars); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package com.woowacourse.racingGame.domain; | ||
|
||
import java.util.stream.Collectors; | ||
|
||
import com.woowacourse.racingGame.utils.StringUtil; | ||
|
||
public class CarsFactory { | ||
public static Cars generate(final String inputCarName, MovableStrategy movableStrategy) { | ||
return new Cars(StringUtil.split(inputCarName).stream() | ||
.map(Name::new) | ||
.map(name -> new Car(name, movableStrategy)) | ||
.collect(Collectors.toList())); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package com.woowacourse.racingGame.domain; | ||
|
||
public interface MovableStrategy { | ||
boolean isMovable(); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package com.woowacourse.racingGame.domain; | ||
|
||
public class MovementNumber { | ||
private static final int ZERO = 0; | ||
|
||
private final int movementNumber; | ||
|
||
public MovementNumber(final String inputMovementNumber) { | ||
final int parsedNumber = checkValid(inputMovementNumber); | ||
checkPositive(parsedNumber); | ||
this.movementNumber = parsedNumber; | ||
} | ||
|
||
private int checkValid(final String number) { | ||
try { | ||
return Integer.parseInt(number); | ||
} catch (NumberFormatException e) { | ||
throw new IllegalArgumentException("입력한 이동 횟수가 숫자가 아닙니다."); | ||
} | ||
} | ||
|
||
private void checkPositive(final int parsedNumber) { | ||
if (parsedNumber <= ZERO) { | ||
throw new IllegalArgumentException("입력한 이동 횟수가 0보다 작은 값입니다."); | ||
} | ||
} | ||
|
||
public int getMovementNumber() { | ||
return this.movementNumber; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
package com.woowacourse.racingGame.domain; | ||
|
||
import java.util.Objects; | ||
|
||
public class Name { | ||
private static final int MAX_NAME_LENGTH_LIMIT = 5; | ||
|
||
private final String name; | ||
|
||
public Name(final String name) { | ||
checkNullOrEmpty(name); | ||
checkValidLength(name); | ||
this.name = name; | ||
} | ||
|
||
private void checkNullOrEmpty(final String name) { | ||
if (Objects.isNull(name) || name.isEmpty()) { | ||
throw new IllegalArgumentException("null 또는 빈 문자열입니다."); | ||
} | ||
} | ||
|
||
private void checkValidLength(final String name) { | ||
if (name.length() > MAX_NAME_LENGTH_LIMIT) { | ||
throw new IllegalArgumentException("이름은 5자이하만 가능합니다."); | ||
} | ||
} | ||
|
||
public String getName() { | ||
return name; | ||
} | ||
|
||
@Override | ||
public boolean equals(Object o) { | ||
if (this == o) | ||
return true; | ||
if (o == null || getClass() != o.getClass()) | ||
return false; | ||
Name name = (Name)o; | ||
return Objects.equals(this.name, name.name); | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return Objects.hash(name); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package com.woowacourse.racingGame.domain; | ||
|
||
public class PlannedMovableStrategy implements MovableStrategy { | ||
private Power power; | ||
|
||
public PlannedMovableStrategy(final Power power) { | ||
this.power = power; | ||
} | ||
|
||
@Override | ||
public boolean isMovable() { | ||
return power.isMovable(); | ||
} | ||
} |
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.
모든 변수에 final을 붙인점이 인상적이네요 👍
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.
final을 적절한 상황에 맞게 붙여야 한다고 들었는데, 제가 너무 여기저기 다 붙여둔 것이 아닌가 싶은 생각이 들었습니다.
혹시 final을 어떠한 상황에서 붙이는 것이 적절한 지 알려주실 수 있으신가요?
참고할 만한 블로그 링크 정도라도 알려주신다면 정말 감사하겠습니다 👍
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.
아래 글을 읽어보세요~!
https://blog.lulab.net/programming-java/java-final-when-should-i-use-it/