-
Notifications
You must be signed in to change notification settings - Fork 93
[자동차 경주] 나성 미션 제출합니다. #79
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
Changes from all commits
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,14 @@ | ||
import controller.RaceController; | ||
import view.RaceOutputView; | ||
|
||
import java.util.Random; | ||
|
||
public class Application { | ||
|
||
public static void main(String[] args) { | ||
Random random = new Random(); | ||
RaceOutputView raceOutputView = new RaceOutputView(); | ||
RaceController raceController = new RaceController(raceOutputView, random); | ||
raceController.start(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package controller; | ||
|
||
import model.Car; | ||
import model.Racing; | ||
import model.dto.RacingPlayResponse; | ||
import view.RaceOutputView; | ||
import view.UserInputView; | ||
|
||
import java.util.Random; | ||
|
||
public class RaceController { | ||
|
||
private final RaceOutputView raceOutputView; | ||
private final Random random; | ||
|
||
public RaceController(RaceOutputView raceOutputView, Random random) { | ||
this.raceOutputView = raceOutputView; | ||
this.random = random; | ||
} | ||
|
||
public void start() { | ||
raceOutputView.inputCarNames(); | ||
String carNames = UserInputView.readStringInput(); | ||
raceOutputView.inputRaceCount(); | ||
int raceCount = UserInputView.readIntegerInput(); | ||
|
||
Racing racing = new Racing(random); | ||
RacingPlayResponse response = racing.play(carNames, raceCount); | ||
|
||
raceOutputView.printRacingData(response.moveData()); | ||
raceOutputView.printWinners(response.winners() | ||
.stream().map(Car::getName).toList()); | ||
Comment on lines
+30
to
+32
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. 컨트롤러에서 dto의 값을 꺼내서 view로 넘기기 보다는, dto 자체를 넘겨서 view에서 값을 꺼내는 것에 대해서는 어떻게 생각하시나요 ?? 컨트롤러에서 dto의 값을 꺼내서 넘겨주면, dto의 의미가 모호해지는 것 같다는 생각이 들었습니다 ! 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. Model -> Controller -> View 를 모두 의존하는 객체를 줄이는 것이 좋다고 생각하였습니다! |
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
package model; | ||
|
||
import java.util.Random; | ||
|
||
public class Car { | ||
|
||
private final Random random; | ||
private final String name; | ||
private int position = 1; | ||
|
||
|
||
public Car(String name, Random random) { | ||
this.name = name; | ||
validateCarName(name); | ||
this.random = random; | ||
} | ||
|
||
private void validateCarName(String name) { | ||
if(name == null || name.isBlank() || name.length() > 5) { | ||
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. 나성님은 3가지 조건에 대해서 하나의 예외 메시지로 처리하셨네요 !
5자 이하인 부분과 입력하지 않는 부분 두 가지로 나눠서 예외 메시지를 처리하면 더 좋은 예외 메시지가 될 것 같은데, 이에 대한 나성님의 생각이 궁금합니다 ! 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. 저도 이 부분에서 null이거나 isBlank일 경우 "이름을 입력해주세요." 로 나누려다가 |
||
throw new IllegalArgumentException("5자 이하의 이름을 작성해주세요."); | ||
} | ||
} | ||
|
||
public void move() { | ||
if (canMove()) { | ||
position++; | ||
} | ||
} | ||
|
||
private boolean canMove() { | ||
return random.nextInt(10) >= 4; | ||
} | ||
|
||
public String getName() { | ||
return name; | ||
} | ||
|
||
public int getPosition() { | ||
return position; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package model; | ||
|
||
import java.util.List; | ||
import java.util.Random; | ||
|
||
public class Cars { | ||
|
||
private final List<Car> cars; | ||
|
||
public Cars(Random random, List<String> carNames) { | ||
this.cars = carNames.stream() | ||
.map(carName -> new Car(carName, random)) | ||
.toList(); | ||
|
||
} | ||
|
||
public void allMove(){ | ||
cars.forEach(Car::move); | ||
} | ||
|
||
public int findMaxDistance(){ | ||
return cars.stream() | ||
.mapToInt(Car::getPosition) | ||
.max() | ||
.orElse(0); | ||
} | ||
|
||
public List<Car> getCars() { | ||
return cars; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
package model; | ||
|
||
import model.dto.RacingPlayResponse; | ||
|
||
import java.util.*; | ||
import java.util.stream.Collectors; | ||
import java.util.stream.IntStream; | ||
|
||
public class Racing { | ||
|
||
private final Random rand; | ||
|
||
public Racing(Random rand) { | ||
this.rand = rand; | ||
} | ||
|
||
public RacingPlayResponse play(String inputCarNames, int raceCount) { | ||
List<String> carNames = Arrays.stream(inputCarNames.split(",")).toList(); | ||
Cars cars = new Cars(rand, carNames); | ||
Comment on lines
+18
to
+19
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. join이라는 메소드로 옮기고, 매개변수로 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. Cars를 만드는 팩토리 클래스를 사용한 후 매개변수로 보내는 것도 좋은 방법인 것 같습니다! |
||
List<Map<String, Integer>> moveData = runRacing(raceCount, cars); | ||
|
||
return new RacingPlayResponse(moveData, getWinners(cars)); | ||
} | ||
|
||
private List<Map<String, Integer>> runRacing(int raceCount, Cars cars) { | ||
List<Map<String, Integer>> moveData = new ArrayList<>(); | ||
moveData.add(getSnapshot(cars)); | ||
IntStream.range(0, raceCount) | ||
.forEach(i -> moveData.add(moveAndGetSnapshot(cars))); | ||
|
||
return moveData; | ||
} | ||
|
||
private Map<String, Integer> moveAndGetSnapshot(Cars cars) { | ||
cars.allMove(); | ||
|
||
return getSnapshot(cars); | ||
} | ||
|
||
private Map<String, Integer> getSnapshot(Cars cars) { | ||
return cars.getCars().stream() | ||
.collect(Collectors.toMap(Car::getName, Car::getPosition)); | ||
} | ||
|
||
private List<Car> getWinners(Cars cars) { | ||
int maxDistance = cars.findMaxDistance(); | ||
|
||
return cars.getCars().stream() | ||
.filter(car -> car.getPosition() == maxDistance) | ||
.toList(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package model.dto; | ||
|
||
import model.Car; | ||
|
||
import java.util.List; | ||
import java.util.Map; | ||
|
||
public record RacingPlayResponse( | ||
List<Map<String, Integer>> moveData, | ||
List<Car> winners | ||
) { | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package view; | ||
|
||
import java.util.List; | ||
import java.util.Map; | ||
|
||
public class RaceOutputView { | ||
|
||
public void inputCarNames() { | ||
System.out.println("경주할 자동차 이름을 입력하세요(이름은 쉼표(,)를 기준으로 구분)."); | ||
} | ||
|
||
public void inputRaceCount() { | ||
System.out.println("시도할 회수는 몇회인가요?"); | ||
} | ||
|
||
public void printRacingData(List<Map<String, Integer>> moveData) { | ||
System.out.println("\n실행 결과"); | ||
for (Map<String, Integer> data : moveData) { | ||
data.forEach( | ||
(key, value) -> | ||
System.out.println(key + " : " + "-".repeat(value)) | ||
); | ||
System.out.println(); | ||
} | ||
} | ||
|
||
public void printWinners(List<String> winnerNames) { | ||
System.out.println(String.join(", ", winnerNames) + "가 최종 우승했습니다."); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package view; | ||
|
||
import java.util.Scanner; | ||
|
||
public class UserInputView { | ||
|
||
private static final Scanner scanner = new Scanner(System.in); | ||
|
||
public static String readStringInput() { | ||
return scanner.nextLine(); | ||
} | ||
|
||
public static int readIntegerInput() { | ||
return scanner.nextInt(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package common; | ||
|
||
import java.util.List; | ||
import java.util.Random; | ||
|
||
public class FakeRandom extends Random { | ||
|
||
private int index; | ||
private final List<Integer> fixedValues; | ||
|
||
public FakeRandom(List<Integer> fixedValues) { | ||
this.fixedValues = fixedValues; | ||
} | ||
|
||
@Override | ||
public int nextInt(int dummyBound) { | ||
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. dummyBound의 역할이 궁금합니다 👀 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. FakeRandom은 랜덤값을 생성자로 받게 되는데 그렇게 되면 nextInt에서 사용하는 인자값은 필요 없게 됩니다. |
||
if(index >= fixedValues.size()) { | ||
throw new IndexOutOfBoundsException(); | ||
} | ||
|
||
return fixedValues.get(index++); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
package controller; | ||
|
||
import common.FakeRandom; | ||
import org.junit.jupiter.api.DisplayName; | ||
import org.junit.jupiter.api.Test; | ||
import view.RaceOutputView; | ||
|
||
import java.io.ByteArrayInputStream; | ||
import java.io.ByteArrayOutputStream; | ||
import java.io.IOException; | ||
import java.io.PrintStream; | ||
|
||
import static fixture.FakeNumber.winnersNumbers; | ||
import static org.assertj.core.api.Assertions.assertThat; | ||
|
||
public class RaceControllerTest { | ||
|
||
private final RaceOutputView outputView = new RaceOutputView(); | ||
|
||
@Test | ||
@DisplayName("OK : 자동차 레이싱 플레이") | ||
void testRace_winners() throws IOException { | ||
RaceController raceController = new RaceController(outputView, new FakeRandom(winnersNumbers)); | ||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); | ||
System.setOut(new PrintStream(outputStream)); | ||
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream("neo,brie,brown\n5\n".getBytes()); | ||
System.setIn(byteArrayInputStream); | ||
|
||
raceController.start(); | ||
|
||
String output = outputStream.toString(); | ||
assertThat(output).contains("경주할 자동차 이름을 입력하세요(이름은 쉼표(,)를 기준으로 구분)."); | ||
assertThat(output).contains("시도할 회수는 몇회인가요?"); | ||
assertThat(output).contains("실행 결과"); | ||
assertThat(output).contains("neo : -"); | ||
assertThat(output).contains("neo : --"); | ||
assertThat(output).contains("neo : ---"); | ||
assertThat(output).contains("neo : ----"); | ||
assertThat(output).contains("neo : -----"); | ||
assertThat(output).contains("brie : -"); | ||
assertThat(output).contains("brie : --"); | ||
assertThat(output).contains("brie : ---"); | ||
assertThat(output).contains("brie : ----"); | ||
assertThat(output).contains("brown : -"); | ||
assertThat(output).contains("brown : --"); | ||
assertThat(output).contains("brown : ---"); | ||
assertThat(output).contains("brown : ----"); | ||
assertThat(output).contains("brown : -----"); | ||
assertThat(output).contains("neo, brown가 최종 우승했습니다."); | ||
byteArrayInputStream.close(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package fixture; | ||
|
||
import java.util.List; | ||
|
||
public class FakeNumber { | ||
|
||
public static final int MOVE_NUMBER = 4; | ||
public static final int STOP_NUMBER = 3; | ||
public static final List<Integer> winnerNumbers = List.of( | ||
MOVE_NUMBER, MOVE_NUMBER, STOP_NUMBER, | ||
STOP_NUMBER, MOVE_NUMBER, STOP_NUMBER, | ||
STOP_NUMBER, MOVE_NUMBER, STOP_NUMBER, | ||
STOP_NUMBER, MOVE_NUMBER, STOP_NUMBER, | ||
STOP_NUMBER, MOVE_NUMBER, STOP_NUMBER | ||
); | ||
public static final List<Integer> winnersNumbers = List.of( | ||
MOVE_NUMBER, STOP_NUMBER, MOVE_NUMBER, | ||
MOVE_NUMBER, MOVE_NUMBER, MOVE_NUMBER, | ||
MOVE_NUMBER, MOVE_NUMBER, MOVE_NUMBER, | ||
MOVE_NUMBER, MOVE_NUMBER, MOVE_NUMBER, | ||
STOP_NUMBER, STOP_NUMBER, STOP_NUMBER | ||
); | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,50 @@ | ||||||||||
package model; | ||||||||||
|
||||||||||
import common.FakeRandom; | ||||||||||
import org.junit.jupiter.api.Assertions; | ||||||||||
import org.junit.jupiter.api.DisplayName; | ||||||||||
import org.junit.jupiter.api.Test; | ||||||||||
import org.junit.jupiter.params.ParameterizedTest; | ||||||||||
import org.junit.jupiter.params.provider.ValueSource; | ||||||||||
|
||||||||||
import java.util.List; | ||||||||||
import java.util.Random; | ||||||||||
|
||||||||||
import static fixture.FakeNumber.MOVE_NUMBER; | ||||||||||
import static fixture.FakeNumber.STOP_NUMBER; | ||||||||||
import static org.assertj.core.api.Assertions.assertThat; | ||||||||||
|
||||||||||
public class CarTest { | ||||||||||
|
||||||||||
@ParameterizedTest() | ||||||||||
@DisplayName("OK : 랜덤값이 4 이상일 시 움직임") | ||||||||||
@ValueSource(ints = {MOVE_NUMBER, 9}) | ||||||||||
void carMove(int fixedValue){ | ||||||||||
Car car = createCar(fixedValue); | ||||||||||
car.move(); | ||||||||||
|
||||||||||
assertThat(car.getPosition()).isEqualTo(2); | ||||||||||
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. 자동차의 초기 위치를 1로 설정하신 이유가 있으실까요 ?? 뭔가 다른 로직이 있어서 2가 예상 값인가 해서 찾아보다가, 초기 값이 1이더라구요 ㅎㅎ,, 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. 예시에서 처음 상태 출력이 - 로 확인하여서 1을 default 값으로 사용하였습니다! |
||||||||||
} | ||||||||||
|
||||||||||
@ParameterizedTest() | ||||||||||
@DisplayName("OK : 랜덤값이 4 미만일 시 움직이지 않음") | ||||||||||
@ValueSource(ints = {0, STOP_NUMBER}) | ||||||||||
void carNotMove(int fixedValue){ | ||||||||||
Car car = createCar(fixedValue); | ||||||||||
car.move(); | ||||||||||
|
||||||||||
assertThat(car.getPosition()).isEqualTo(1); | ||||||||||
} | ||||||||||
|
||||||||||
@Test | ||||||||||
@DisplayName("ERROR : 이름 형식이 잘못됨") | ||||||||||
void InvalidNameCar(){ | ||||||||||
Assertions.assertThrows(IllegalArgumentException.class, () -> new Car("", new Random())); | ||||||||||
Assertions.assertThrows(IllegalArgumentException.class, () -> new Car(" ", new Random())); | ||||||||||
Assertions.assertThrows(IllegalArgumentException.class, () -> new Car("test12", new Random())); | ||||||||||
} | ||||||||||
|
||||||||||
private Car createCar(int fixedValue){ | ||||||||||
return new Car("test1", new FakeRandom(List.of(fixedValue))); | ||||||||||
Comment on lines
+47
to
+48
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. 파라미터를 바꾸면 FakeRandom를 더 잘 활용할 수 있을 거 같다는 생각이 들었습니다 ! 이렇게 하면, 뭔가 테스트를 더 다양하게 할 수 있을 거 같아요. 가령, A차, B차, C차가 있으면 승자를 예측할 수 있는 테스트도 할 수 있을 거 같아요 카 테스트 보다는 레이싱 테스트에서 사용할 수 있을 거 같아요 !
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. 그렇게 한다면 Fake 객체를 더 다양하고 잘 사용할 수 있을 것 같아요!! |
||||||||||
} | ||||||||||
} |
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.
컨트롤러에서 객체 생성하는 대신, 주입 받는 방법에 대해서 나성님은 어떻게 생각하시나요 ??
개인적으로 컨트롤러의 객체 생성 역할이 애매하다고 생각이 들었고, Racing 객체가 변경되면 컨트롤러에 들어가서 수정을 해야하는 소요가 발생할 것 같습니다 !
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.
Racing은 Service 역할을 하는 VO 객체라고 생각하여 단순하게 객체를 생성하였는데 주입하는 방식이 더 좋은 것 같네요!