-
Notifications
You must be signed in to change notification settings - Fork 7
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
[자동차 경주] 베디 #1
[자동차 경주] 베디 #1
Changes from 28 commits
250dc42
1f1b185
33bceb2
bcbe5b5
42111ac
81b6874
7ef12bb
e538057
6b0ff66
1323fbe
b738501
f45db21
48d6c6e
5aa31ed
7f9766b
5061815
0dd39e3
0942191
531f355
5d58965
b083a2b
7a452ed
6e81dd1
83db448
4afecb7
24b7e46
cc83425
2fb4281
65b3ba2
b0ca4ab
ec4a347
afb53fe
edd5e30
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 |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package racingcar.console; | ||
|
||
import racingcar.console.view.InputView; | ||
import racingcar.console.view.OutputView; | ||
import racingcar.domain.car.Cars; | ||
import racingcar.domain.movestrategy.MoveStrategy; | ||
import racingcar.domain.movestrategy.RandomNumberMoveStrategy; | ||
import racingcar.domain.race.RaceResult; | ||
import racingcar.service.RacingService; | ||
|
||
public class App { | ||
public static void main(String[] args) { | ||
final MoveStrategy moveStrategy = new RandomNumberMoveStrategy(); | ||
final RacingService racingService = new RacingService(moveStrategy); | ||
|
||
final String names = InputView.inputNames(); | ||
final Cars cars = racingService.createCars(names); | ||
|
||
final int repeatNumber = InputView.inputRepeatNumber(); | ||
final RaceResult raceResult = racingService.startRace(repeatNumber, cars); | ||
|
||
OutputView.printRaceResult(raceResult); | ||
OutputView.printWinner(raceResult.getWinners()); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package racingcar.console.view; | ||
|
||
import java.util.Scanner; | ||
|
||
public class InputView { | ||
public static final Scanner scanner = new Scanner(System.in); | ||
|
||
private InputView() { | ||
} | ||
Comment on lines
+8
to
+9
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 기본 생성자를 써준 이유를 알 수 있을까요? 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. 모든 메소드가 static 이여서 인스턴스를 생성할 필요가 없습니다. 이를 방지하기 위해서 private으로 해줬습니다. |
||
|
||
public static String inputNames() { | ||
System.out.println("경주할 자동차 이름을 입력하세요(이름은 쉼표(,)를 기준으로 구분)"); | ||
return scanner.nextLine(); | ||
} | ||
|
||
public static int inputRepeatNumber() { | ||
System.out.println("시도할 회수는 몇회인가요?"); | ||
return scanner.nextInt(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
package racingcar.console.view; | ||
|
||
import racingcar.domain.race.RaceResult; | ||
import racingcar.service.dto.CarDto; | ||
|
||
import java.util.List; | ||
import java.util.stream.Collectors; | ||
|
||
public class OutputView { | ||
|
||
private static final String ROUTE = "-"; | ||
|
||
private OutputView() { | ||
} | ||
|
||
public static void printRaceResult(final RaceResult raceResult) { | ||
StringBuilder sb = new StringBuilder(); | ||
sb.append("실행 결과\n"); | ||
while (raceResult.hasNext()) { | ||
printCurrentRace(raceResult.next(), sb); | ||
} | ||
System.out.println(sb.toString()); | ||
} | ||
|
||
private static void printCurrentRace(final List<CarDto> carDtos, final StringBuilder sb) { | ||
carDtos.forEach(carDto -> printCar(sb, carDto)); | ||
sb.append("\n"); | ||
} | ||
|
||
private static void printCar(final StringBuilder sb, final CarDto carDto) { | ||
sb.append(String.format("%s : ", carDto.getName())); | ||
for (int i = 0; i < carDto.getPosition(); i++) { | ||
sb.append(ROUTE); | ||
} | ||
sb.append("\n"); | ||
} | ||
|
||
|
||
public static void printWinner(final List<CarDto> winners) { | ||
final String joinedWinners = winners.stream() | ||
.map(CarDto::getName) | ||
.collect(Collectors.joining(", ")); | ||
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. joining 사용 👍 |
||
|
||
System.out.println(String.format("%s가 최종 우승했습니다.", joinedWinners)); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package racingcar.domain.car; | ||
|
||
import racingcar.domain.movestrategy.MoveStrategy; | ||
|
||
public class Car { | ||
private final Name name; | ||
private Position position; | ||
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 Car(final String name) { | ||
this.name = Name.of(name); | ||
this.position = Position.newInstance(); | ||
} | ||
|
||
public static Car of(final String name) { | ||
return new Car(name); | ||
} | ||
|
||
void tryMove(final MoveStrategy moveStrategy) { | ||
if (moveStrategy.isAvailableMove(this)) { | ||
position = position.increase(); | ||
} | ||
} | ||
|
||
public String getName() { | ||
return name.getName(); | ||
} | ||
|
||
public int getPosition() { | ||
return position.getPosition(); | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return "Car{" + | ||
"name='" + name + '\'' + | ||
", position=" + position + | ||
'}'; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package racingcar.domain.car; | ||
|
||
import racingcar.domain.movestrategy.MoveStrategy; | ||
|
||
import java.util.Collections; | ||
import java.util.List; | ||
import java.util.stream.Collectors; | ||
import java.util.stream.Stream; | ||
|
||
public class Cars { | ||
public static final int MINIMUM_NAMES = 2; | ||
private static final String DELIMITER = ","; | ||
|
||
private final List<Car> cars; | ||
|
||
public Cars(final String names) { | ||
this.cars = createCars(names); | ||
validateCarSize(); | ||
} | ||
|
||
private void validateCarSize() { | ||
if (cars.size() < MINIMUM_NAMES) { | ||
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. 저는 if 조건을 메서드화 시키는게 좋다고 생각하는데, |
||
throw new IllegalArgumentException(MINIMUM_NAMES + "명 이상 입력해주세요."); | ||
} | ||
} | ||
|
||
private List<Car> createCars(final String names) { | ||
return Stream.of(names.split(DELIMITER)) | ||
.map(Car::new) | ||
.collect(Collectors.toList()); | ||
} | ||
|
||
public List<Car> tryMove(final MoveStrategy moveStrategy) { | ||
cars.forEach(car -> car.tryMove(moveStrategy)); | ||
return Collections.unmodifiableList(cars); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
package racingcar.domain.car; | ||
|
||
import java.util.Objects; | ||
|
||
public class Name { | ||
private static final int MAX_BOUNDARY = 5; | ||
private static final int MIN_BOUNDARY = 2; | ||
|
||
private final String name; | ||
|
||
private Name(final String name) { | ||
this.name = name.trim(); | ||
validateLengthOfName(); | ||
} | ||
|
||
private void validateLengthOfName() { | ||
if (this.name.length() > MAX_BOUNDARY || this.name.length() < MIN_BOUNDARY) { | ||
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. 여기에도 아까 말씀 드렸듯이 if 조건을 메서드로 분리 시키면 어떨까요? 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. 메소드가 복잡하면 좋은 방법이라고 생각하지만, 현재는 이 로직밖에 없는데 또 분리시키면 복잡하지 않을까요? 현재 메소드명으로 충분히 설명이 가능하다고 생각해요 |
||
throw new IllegalArgumentException(String.format("이름은 %d~%d자로 해주세요.", MIN_BOUNDARY, MAX_BOUNDARY)); | ||
} | ||
} | ||
|
||
static Name of(final String name) { | ||
return new Name(name); | ||
} | ||
|
||
String getName() { | ||
return name; | ||
} | ||
|
||
@Override | ||
public boolean equals(final Object o) { | ||
if (this == o) return true; | ||
if (o == null || getClass() != o.getClass()) return false; | ||
final Name name1 = (Name) o; | ||
return Objects.equals(name, name1.name); | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return Objects.hash(name); | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return "name= " + name; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package racingcar.domain.car; | ||
|
||
import java.util.HashMap; | ||
import java.util.Map; | ||
|
||
public class Position { | ||
private static final int DEFAULT_CACHE_SIZE = 30; | ||
static final int UNIT_INCREASE = 1; | ||
static final int DEFAULT_POSITION = 0; | ||
|
||
private static final Map<Integer, Position> CACHE = new HashMap<>(); | ||
|
||
|
||
static { | ||
for (int i = DEFAULT_POSITION; i < DEFAULT_CACHE_SIZE; i += UNIT_INCREASE) { | ||
CACHE.put(i, new Position(i)); | ||
} | ||
} | ||
|
||
private final int position; | ||
|
||
private Position(final int position) { | ||
this.position = position; | ||
} | ||
|
||
static Position newInstance() { | ||
return CACHE.get(DEFAULT_POSITION); | ||
} | ||
|
||
Position increase() { | ||
final int increasedPosition = position + UNIT_INCREASE; | ||
return CACHE.getOrDefault(increasedPosition, create(increasedPosition)); | ||
} | ||
|
||
private Position create(final int position) { | ||
final Position createdPosition = new Position(position); | ||
CACHE.put(position, createdPosition); | ||
return createdPosition; | ||
} | ||
|
||
int getPosition() { | ||
return position; | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return "position= " + position; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package racingcar.domain.common; | ||
|
||
public class RepeatNumber { | ||
private static final int NUMBER_BOUNDARY = 1; | ||
|
||
private final int number; | ||
|
||
public RepeatNumber(final int number) { | ||
if (number < NUMBER_BOUNDARY) { | ||
throw new IllegalArgumentException(NUMBER_BOUNDARY + " 이상 입력해주세요."); | ||
} | ||
this.number = number; | ||
} | ||
|
||
public static RepeatNumber from(final int number) { | ||
return new RepeatNumber(number); | ||
} | ||
|
||
|
||
public static RepeatNumber from(final String number) { | ||
return new RepeatNumber(Integer.parseInt(number)); | ||
} | ||
|
||
public int getNumber() { | ||
return number; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package racingcar.domain.exception; | ||
|
||
import racingcar.domain.car.Cars; | ||
|
||
public class RaceStatusEmptyException extends IllegalStateException { | ||
public RaceStatusEmptyException() { | ||
super("CarDto가 비어 있습니다." + Cars.MINIMUM_NAMES + "명 이상이어야 합니다."); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package racingcar.domain.movestrategy; | ||
|
||
import racingcar.domain.car.Car; | ||
|
||
public interface MoveStrategy { | ||
boolean isAvailableMove(final Car 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. 메서드 인자로 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. 처음엔 테스트를 수월하게 하기 위해서 넣었습니다. (car의 이름이 pobi일 때만 true) 이런식으로 하기 위해서요. 하지만 테스트를 위한 코드라서 다시 수정 하려다가 나중에 경우에 따라서 car가 필요할 수 도 있겠다 싶어서 유지했습니다. |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package racingcar.domain.movestrategy; | ||
|
||
import racingcar.domain.car.Car; | ||
|
||
import java.util.Random; | ||
|
||
public class RandomNumberMoveStrategy implements MoveStrategy { | ||
private static final int MOVE_BOUNDARY = 4; | ||
private static final int RANDOM_MAX_BOUNDARY = 9; | ||
|
||
private final Random random = new Random(); | ||
|
||
@Override | ||
public boolean isAvailableMove(final Car car) { | ||
return random.nextInt(RANDOM_MAX_BOUNDARY + 1) >= MOVE_BOUNDARY; | ||
} | ||
} |
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.
실행 결과 예외 상황이 조금 더 있을 수도 있을 것 같은데요.
제가 조금 빡빡하게 예외를 체크하는 편이라 그렇지만
중복된 자동차 이름
이 있을 경우red, blue, green
이런 식으로 공백을 포함하여 사용자가 이름을 입력하는 경우이런 경우에 대해서는 어떻게 생각하세요?
크게 동의하지 않으시면 넘어가주셔도 좋습니다 :)
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.
그리고 현재 예외가 발생하면 그냥 런타임 에러가 콘솔에 표시되면서 터지는데요.
에러 로그를 날것으로 보여주는 것보다는
try-catch로 잡아서 OutputView를 통해 예외 상황을 사용자에게 알려주는 것은 어떨까 제안드려 봅니다 :)
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.
1.
중복된 자동차 이름이 있을 경우
일부러 허용해줬습니다.2.
red, blue, green
공백은 오랜만에 해서 감을 잃었는지 미처 생각을 못했네요.3. 제가 예외처리에 소홀했네요.
좋은 피드백 감사합니다 ㅎㅎ