-
Notifications
You must be signed in to change notification settings - Fork 74
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
[자동차 경주] 정예림 미션 제출합니다. #75
Changes from all commits
0195109
bcbb212
b284921
8f353f7
9e1d1ce
c48c52f
3a0cdf9
98042a6
b3eb62e
dd1ad64
30dc004
5d79c53
ba5e1e6
6af8b51
b82c25f
915138b
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,9 @@ | ||
import controller.RacingCarApplication; | ||
|
||
public class Main { | ||
public static void main(String[] args) { | ||
|
||
// 레이싱 게임 애플리케이션 실행 | ||
new RacingCarApplication().run(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package controller; | ||
|
||
import domain.Race; | ||
import domain.RacingCar; | ||
import view.InputView; | ||
import view.ResultView; | ||
|
||
import java.util.List; | ||
|
||
public class RacingCarApplication { | ||
|
||
// 레이싱 게임 실행 | ||
public void run() { | ||
try { | ||
List<RacingCar> cars = InputView.getCarNames(); | ||
int rounds = InputView.getRoundsFromUser(); | ||
Comment on lines
+15
to
+16
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.
|
||
|
||
Race race = new Race(cars); | ||
race.race(rounds); | ||
|
||
ResultView.printRaceResults(race); | ||
ResultView.printWinners(race.findWinners()); | ||
} catch (IllegalArgumentException e) { | ||
System.out.println("오류: " + e.getMessage()); | ||
} | ||
Comment on lines
+14
to
+25
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. 이러한 방식은 좋지 않습니다. |
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
package domain; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
public class Race { | ||
private final List<RacingCar> 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. 컬렉션은 null 안정성을 위해 필드에서 바로 초기화하는게 좋습니다. |
||
|
||
public Race(List<RacingCar> cars) { | ||
this.cars = new ArrayList<>(cars); | ||
} | ||
|
||
// 모든 자동차를 한 번씩 이동 | ||
public void moveCars() { | ||
for (RacingCar car : cars) { | ||
car.move(); | ||
} | ||
} | ||
|
||
public void race(int rounds) { | ||
for (int i = 0; i < rounds; i++) { | ||
moveCars(); | ||
} | ||
} | ||
|
||
public List<RacingCar> getCars() { | ||
return cars; | ||
} | ||
|
||
// 자동차들 중 최대 위치를 반환 | ||
private int getMaxPosition() { | ||
int max = 0; | ||
for (RacingCar car : cars) { | ||
if (car.getPosition() > max) { | ||
max = car.getPosition(); | ||
} | ||
} | ||
return max; | ||
} | ||
|
||
// 최대 위치에 도달한 자동차 목록 반환 | ||
public List<RacingCar> findWinners() { | ||
int maxPosition = getMaxPosition(); | ||
List<RacingCar> winners = new ArrayList<>(); | ||
|
||
for (RacingCar car : cars) { | ||
if (car.getPosition() == maxPosition) { | ||
winners.add(car); | ||
} | ||
} | ||
return winners; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
package domain; | ||
|
||
import java.util.Random; | ||
|
||
public class RacingCar { | ||
private final String name; | ||
private final Random random; | ||
private int position; | ||
|
||
// Random 객체를 생성자에서 받아옴 | ||
public RacingCar(String name, Random random) { | ||
verifyCarName(name); | ||
this.name = name; | ||
this.random = random; | ||
this.position = 0; | ||
} | ||
|
||
private void verifyCarName(String name) { | ||
if (name.isEmpty() || name.length() > 5) { | ||
throw new IllegalArgumentException("자동차 이름은 1~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. 커스텀 예외에 대해서도 알아보시면 좋을 것 같아요 |
||
} | ||
} | ||
|
||
public String getName() { | ||
return name; | ||
} | ||
|
||
public int getPosition() { | ||
return position; | ||
} | ||
|
||
// 난수 0~9중 4 이상이 나오면 전진 | ||
public boolean move() { | ||
int randomValue = random.nextInt(10); | ||
if (randomValue >= 4) { | ||
position++; | ||
return true; // 전진 성공 | ||
} | ||
return false; // 전진 실패 (정지) | ||
} | ||
Comment on lines
+33
to
+40
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. 전진 성공 실패로 인하여 로직이 달라지지 않는데 boolean을 반환해줄 이유가 있을까요? |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package view; | ||
|
||
import domain.RacingCar; | ||
|
||
import java.util.List; | ||
import java.util.Scanner; | ||
import java.util.Random; | ||
import java.util.ArrayList; | ||
|
||
public class InputView { | ||
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 final Scanner scanner = new Scanner(System.in); | ||
private static final Random random = new Random(); | ||
|
||
public static List<RacingCar> getCarNames() { | ||
System.out.println("경주할 자동차 이름을 입력하세요(이름은 쉼표(,)로 구분):"); | ||
String input = scanner.nextLine(); | ||
String[] carNames = input.split(","); | ||
List<RacingCar> cars = new ArrayList<>(); | ||
|
||
for (String name : carNames) { | ||
name = name.trim(); | ||
cars.add(new RacingCar(name, random)); | ||
} | ||
return cars; | ||
} | ||
|
||
public static int getRoundsFromUser() { | ||
System.out.println("시도할 횟수는 몇 회인가요?"); | ||
while (!scanner.hasNextInt()) { | ||
System.out.println("숫자를 입력해야 합니다. 다시 입력하세요:"); | ||
scanner.nextLine(); | ||
} | ||
Comment on lines
+29
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. 👍 |
||
|
||
int rounds = scanner.nextInt(); | ||
scanner.nextLine(); | ||
|
||
if (rounds <= 0) { | ||
throw new IllegalArgumentException("시도 횟수는 1 이상의 정수여야 합니다."); | ||
} | ||
Comment on lines
+37
to
+39
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. 해당 예외는 Race에 있는게 적절할 것 같습니다. 이유를 설명하는것은 숙제로 드리겠습니다 📖 |
||
|
||
return rounds; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package view; | ||
|
||
import domain.Race; | ||
import domain.RacingCar; | ||
|
||
import java.util.List; | ||
|
||
public class ResultView { | ||
public static void printRaceResults(Race race) { | ||
System.out.println("\n실행 결과:"); | ||
for (RacingCar car : race.getCars()) { | ||
System.out.print(car.getName() + " : "); | ||
System.out.println("-".repeat(car.getPosition())); // 위치만큼 '-' 출력 | ||
} | ||
} | ||
|
||
public static void printWinners(List<RacingCar> winners) { | ||
System.out.println(String.join(", ", getWinnerNames(winners)) + "가 최종 우승했습니다."); | ||
} | ||
|
||
private static List<String> getWinnerNames(List<RacingCar> winners) { | ||
return winners.stream() | ||
.map(RacingCar::getName) | ||
.toList(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
import java.util.List; | ||
import java.util.Random; | ||
|
||
import domain.Race; | ||
import domain.RacingCar; | ||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.Test; | ||
import org.junit.jupiter.api.DisplayName; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
|
||
@DisplayName("우승 자동차 구하기 테스트") | ||
class RaceTest { | ||
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. 해당 클래스는 들여쓰기 길이가 다른것 같습니다. 체크해주세요! |
||
// 항상 전진하는 moveRandom 객체 생성 | ||
private static final Random moveRandom = new Random() { | ||
@Override | ||
public int nextInt(int bound) { | ||
return 4; | ||
} | ||
}; | ||
// 항상 정지하는 stopRandom 객체 생성 | ||
private static final Random stopRandom = new Random() { | ||
@Override | ||
public int nextInt(int bound) { | ||
return 3; | ||
} | ||
}; | ||
Comment on lines
+14
to
+27
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 RacingCar carA; | ||
private RacingCar carB; | ||
private Race race; | ||
|
||
@BeforeEach | ||
void setup() { | ||
// CarA는 매 라운드마다 전진하고, CarB는 매 라운드마다 정지함 | ||
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. 그렇다면 차 이름을 |
||
carA = new RacingCar("CarA", moveRandom); | ||
carB = new RacingCar("CarB", stopRandom); | ||
race = new Race(List.of(carA, carB)); | ||
} | ||
|
||
@DisplayName("race()가 라운드 수만큼 자동차를 전진/정지 시키는가?") | ||
@Test | ||
void checkCarsMoveCountAfterRace() { | ||
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. DisplayName을 사용하지않고 한글로 테스트명을 작성하는 것도 좋습니다 |
||
race.race(5); | ||
|
||
assertThat(carA.getPosition()) | ||
.as("CarA가 5번의 라운드 동안 5번 전진해야 합니다.") | ||
.isEqualTo(5); | ||
|
||
assertThat(carB.getPosition()) | ||
.as("CarB가 5번의 라운드 동안 5번 정지해야 합니다.") | ||
.isEqualTo(0); | ||
} | ||
|
||
@DisplayName("findWinners() 메서드가 올바른 우승자를 반환하는가?") | ||
@Test | ||
void verifyCorrectWinners() { | ||
race.race(5); | ||
List<RacingCar> winners = race.findWinners(); | ||
|
||
assertThat(winners) | ||
.hasSize(1) | ||
.extracting(RacingCar::getName) | ||
.containsExactly("CarA"); | ||
} | ||
|
||
@DisplayName("동점인 경우, 우승자가 여러 명이 나오는가?") | ||
@Test | ||
void checkMultipleWinners() { | ||
// CarB도 항상 전진하도록 설정 (동점 상황) | ||
carB = new RacingCar("CarB", moveRandom); | ||
race = new Race(List.of(carA, carB)); | ||
|
||
race.race(5); | ||
List<RacingCar> winners = race.findWinners(); | ||
|
||
assertThat(winners) | ||
.hasSize(2) | ||
.extracting(RacingCar::getName) | ||
.containsExactly("CarA", "CarB"); | ||
} | ||
|
||
@DisplayName("모든 자동차가 정지한 경우, 전부 우승자로 나오는가?") | ||
@Test | ||
void allCarsStopped_AllAreWinners() { | ||
// CarA, CarB 둘 다 정지하도록 설정 | ||
carA = new RacingCar("CarA", stopRandom); | ||
carB = new RacingCar("CarB", stopRandom); | ||
race = new Race(List.of(carA, carB)); | ||
|
||
race.race(5); | ||
List<RacingCar> winners = race.findWinners(); | ||
|
||
assertThat(winners) | ||
.hasSize(2) | ||
.extracting(RacingCar::getName) | ||
.containsExactly("CarA", "CarB"); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
import java.util.Random; | ||
|
||
import domain.RacingCar; | ||
import org.junit.jupiter.api.Test; | ||
import org.junit.jupiter.api.DisplayName; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
import static org.assertj.core.api.Assertions.assertThatThrownBy; | ||
|
||
@DisplayName("움직이는 자동차 테스트") | ||
class RacingCarTest { | ||
private static final String TEST_CAR_NAME = "car"; | ||
|
||
// 고정된 난수 값을 반환하는 MockRandom 클래스 (Random 상속) | ||
private static class MockRandom extends Random { | ||
private final int mockedValue; | ||
|
||
public MockRandom(int mockedValue) { | ||
this.mockedValue = mockedValue; | ||
} | ||
|
||
@Override | ||
public int nextInt(int bound) { | ||
return mockedValue; | ||
} | ||
} | ||
|
||
private RacingCar carWithMockRandom(int mockedValue) { | ||
return new RacingCar(TEST_CAR_NAME, new MockRandom(mockedValue)); | ||
} | ||
|
||
@DisplayName("자동차 이름이 5자를 초과하면 예외가 발생하는가?") | ||
@Test | ||
void checkCarNameIsNotOver5Letters() { | ||
assertThatThrownBy(() -> new RacingCar("Boxster", new MockRandom(4))) | ||
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. 오.. 포르쉐 박스터..! 🚗 |
||
.isInstanceOf(IllegalArgumentException.class) | ||
.hasMessageContaining("자동차 이름은 1~5자만 가능합니다."); | ||
} | ||
|
||
@DisplayName("랜덤값이 4 이상일 경우, 항상 전진하는가?") | ||
@Test | ||
void moveWhenValueIsAtLeast4() { | ||
int[] testValues = {4, 5, 6, 7, 8, 9}; // 전진해야 하는 테스트 값 | ||
|
||
for (int mockedValue : testValues) { | ||
RacingCar car = carWithMockRandom(mockedValue); | ||
boolean isMoved = car.move(); | ||
|
||
assertThat(isMoved) | ||
.as("랜덤 값이 %d일 때 자동차가 전진해야 합니다.", mockedValue) | ||
.isTrue(); | ||
} | ||
Comment on lines
+43
to
+52
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. 참신한 테스트 방식이네요 👍 |
||
} | ||
|
||
@DisplayName("랜덤값이 3 이하일 경우, 항상 정지하는가?") | ||
@Test | ||
void stopWhenValueIsLessThan4() { | ||
int[] testValues = {0, 1, 2, 3}; // 정지해야 하는 테스트 값 | ||
|
||
for (int mockedValue : testValues) { | ||
RacingCar car = carWithMockRandom(mockedValue); | ||
boolean isMoved = car.move(); | ||
|
||
assertThat(isMoved) | ||
.as("랜덤 값이 %d일 때 자동차가 정지해야 합니다.", mockedValue) | ||
.isFalse(); | ||
} | ||
} | ||
} |
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.
코드로 이미 설명이 된다면 주석이 굳이 필요할까도 고민해보셔도 좋을 것 같습니다. 다른 부분도 포함입니담